8674 lines
213 KiB
C
8674 lines
213 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2019 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 "awk-prv.h"
|
|
#include <qse/cmn/fmt.h>
|
|
#include <qse/cmn/mbwc.h>
|
|
|
|
#ifdef DEBUG_RUN
|
|
#include <qse/cmn/sio.h>
|
|
#endif
|
|
|
|
#define PRINT_IOERR -99
|
|
|
|
#define CMP_ERROR -99
|
|
#define DEF_BUF_CAPA 256
|
|
#define RTX_STACK_INCREMENT 512
|
|
|
|
/* Don't forget to grow IDXBUFSIZE if qse_awk_int_t is very large */
|
|
#if (QSE_AWK_SIZEOF_INT_T <= 16) /* 128 bits */
|
|
# define IDXBUFSIZE 64
|
|
#elif (QSE_AWK_SIZEOF_INT_T <= 32) /* 256 bits */
|
|
# define IDXBUFSIZE 128
|
|
#elif (QSE_AWK_SIZEOF_INT_T <= 64) /* 512 bits */
|
|
# define IDXBUFSIZE 192
|
|
#elif (QSE_AWK_SIZEOF_INT_T <= 128) /* 1024 bits */
|
|
# define IDXBUFSIZE 384
|
|
#elif (QSE_AWK_SIZEOF_INT_T <= 256) /* 2048 bits */
|
|
# define IDXBUFSIZE 640
|
|
#else
|
|
# error unsupported. qse_awk_int_t too big
|
|
#endif
|
|
|
|
enum exit_level_t
|
|
{
|
|
EXIT_NONE,
|
|
EXIT_BREAK,
|
|
EXIT_CONTINUE,
|
|
EXIT_FUNCTION,
|
|
EXIT_NEXT,
|
|
EXIT_GLOBAL,
|
|
EXIT_ABORT
|
|
};
|
|
|
|
struct pafv_t
|
|
{
|
|
qse_awk_val_t** args;
|
|
qse_size_t nargs;
|
|
const qse_char_t* argspec;
|
|
};
|
|
|
|
#define DEFAULT_CONVFMT QSE_T("%.6g")
|
|
#define DEFAULT_FS QSE_T(" ")
|
|
#define DEFAULT_OFMT QSE_T("%.6g")
|
|
#define DEFAULT_OFS QSE_T(" ")
|
|
#define DEFAULT_ORS QSE_T("\n")
|
|
#define DEFAULT_ORS_CRLF QSE_T("\r\n")
|
|
#define DEFAULT_SUBSEP QSE_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 qse_size_t as the reference is represented
|
|
* in a pointer variable of qse_awk_val_ref_t and sizeof(void*) is
|
|
* equal to sizeof(qse_size_t). */
|
|
#define IS_VALID_POSIDX(idx) \
|
|
((idx) >= 0 && \
|
|
(idx) <= QSE_TYPE_MAX(qse_awk_int_t) && \
|
|
(idx) <= QSE_TYPE_MAX(qse_size_t))
|
|
|
|
#define SETERR_ARGX_LOC(rtx,code,ea,loc) \
|
|
qse_awk_rtx_seterror ((rtx), (code), (ea), (loc))
|
|
|
|
#define CLRERR(rtx) SETERR_ARGX_LOC(rtx,QSE_AWK_ENOERR,QSE_NULL,QSE_NULL)
|
|
|
|
#define SETERR_ARG_LOC(rtx,code,ep,el,loc) \
|
|
do { \
|
|
qse_cstr_t __ea; \
|
|
__ea.len = (el); __ea.ptr = (ep); \
|
|
qse_awk_rtx_seterror ((rtx), (code), &__ea, (loc)); \
|
|
} while (0)
|
|
|
|
#define SETERR_ARGX(rtx,code,ea) SETERR_ARGX_LOC(rtx,code,ea,QSE_NULL)
|
|
#define SETERR_ARG(rtx,code,ep,el) SETERR_ARG_LOC(rtx,code,ep,el,QSE_NULL)
|
|
#define SETERR_LOC(rtx,code,loc) SETERR_ARGX_LOC(rtx,code,QSE_NULL,loc)
|
|
#define SETERR_COD(rtx,code) SETERR_ARGX_LOC(rtx,code,QSE_NULL,QSE_NULL)
|
|
|
|
#define ADJERR_LOC(rtx,l) do { (rtx)->errinf.loc = *(l); } while (0)
|
|
|
|
static qse_size_t push_arg_from_vals (qse_awk_rtx_t* rtx, qse_awk_nde_fncall_t* call, void* data);
|
|
static qse_size_t push_arg_from_nde (qse_awk_rtx_t* rtx, qse_awk_nde_fncall_t* call, void* data);
|
|
|
|
static int init_rtx (qse_awk_rtx_t* rtx, qse_awk_t* awk, qse_awk_rio_t* rio);
|
|
static void fini_rtx (qse_awk_rtx_t* rtx, int fini_globals);
|
|
|
|
static int init_globals (qse_awk_rtx_t* rtx);
|
|
static void refdown_globals (qse_awk_rtx_t* run, int pop);
|
|
|
|
static int run_pblocks (qse_awk_rtx_t* rtx);
|
|
static int run_pblock_chain (qse_awk_rtx_t* rtx, qse_awk_chain_t* cha);
|
|
static int run_pblock (qse_awk_rtx_t* rtx, qse_awk_chain_t* cha, qse_size_t bno);
|
|
static int run_block (qse_awk_rtx_t* rtx, qse_awk_nde_blk_t* nde);
|
|
static int run_statement (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static int run_if (qse_awk_rtx_t* rtx, qse_awk_nde_if_t* nde);
|
|
static int run_while (qse_awk_rtx_t* rtx, qse_awk_nde_while_t* nde);
|
|
static int run_for (qse_awk_rtx_t* rtx, qse_awk_nde_for_t* nde);
|
|
static int run_foreach (qse_awk_rtx_t* rtx, qse_awk_nde_foreach_t* nde);
|
|
static int run_break (qse_awk_rtx_t* rtx, qse_awk_nde_break_t* nde);
|
|
static int run_continue (qse_awk_rtx_t* rtx, qse_awk_nde_continue_t* nde);
|
|
static int run_return (qse_awk_rtx_t* rtx, qse_awk_nde_return_t* nde);
|
|
static int run_exit (qse_awk_rtx_t* rtx, qse_awk_nde_exit_t* nde);
|
|
static int run_next (qse_awk_rtx_t* rtx, qse_awk_nde_next_t* nde);
|
|
static int run_nextfile (qse_awk_rtx_t* rtx, qse_awk_nde_nextfile_t* nde);
|
|
static int run_delete (qse_awk_rtx_t* rtx, qse_awk_nde_delete_t* nde);
|
|
static int run_reset (qse_awk_rtx_t* rtx, qse_awk_nde_reset_t* nde);
|
|
static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde);
|
|
static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde);
|
|
|
|
static int output_formatted (
|
|
qse_awk_rtx_t* run, int out_type, const qse_char_t* dst,
|
|
const qse_char_t* fmt, qse_size_t fmt_len, qse_awk_nde_t* args);
|
|
static int output_formatted_bytes (
|
|
qse_awk_rtx_t* run, int out_type, const qse_char_t* dst,
|
|
const qse_mchar_t* fmt, qse_size_t fmt_len, qse_awk_nde_t* args);
|
|
|
|
static qse_awk_val_t* eval_expression (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_expression0 (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
static qse_awk_val_t* eval_group (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
static qse_awk_val_t* eval_assignment (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* do_assignment (qse_awk_rtx_t* rtx, qse_awk_nde_t* var, qse_awk_val_t* val);
|
|
static qse_awk_val_t* do_assignment_nonidx (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var, qse_awk_val_t* val);
|
|
static qse_awk_val_t* do_assignment_idx (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var, qse_awk_val_t* val);
|
|
static qse_awk_val_t* do_assignment_pos (qse_awk_rtx_t* rtx, qse_awk_nde_pos_t* pos, qse_awk_val_t* val);
|
|
|
|
static qse_awk_val_t* eval_binary (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_binop_lor (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right);
|
|
static qse_awk_val_t* eval_binop_land (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right);
|
|
static qse_awk_val_t* eval_binop_in (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right);
|
|
static qse_awk_val_t* eval_binop_bor (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_bxor (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_band (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
|
|
static qse_awk_val_t* eval_binop_teq (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_tne (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_eq (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_ne (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_gt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_ge (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_lt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_le (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_lshift (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_rshift (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_plus (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_minus (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_mul (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_div (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_idiv (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_mod (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_exp (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_concat (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
static qse_awk_val_t* eval_binop_ma (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right);
|
|
static qse_awk_val_t* eval_binop_nm (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right);
|
|
|
|
static qse_awk_val_t* eval_unary (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_incpre (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_incpst (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_cnd (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
static qse_awk_val_t* eval_fncall_fun_ex (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, void(*errhandler)(void*), void* eharg);
|
|
|
|
static qse_awk_val_t* eval_fncall_fnc (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_fncall_fun (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_fncall_var (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
static qse_awk_val_t* __eval_call (
|
|
qse_awk_rtx_t* rtx,
|
|
qse_awk_nde_t* nde,
|
|
qse_awk_fun_t* fun,
|
|
qse_size_t(*argpusher)(qse_awk_rtx_t*,qse_awk_nde_fncall_t*,void*),
|
|
void* apdata, /* data to argpusher */
|
|
void(*errhandler)(void*),
|
|
void* eharg);
|
|
|
|
static int get_reference (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, qse_awk_val_t*** ref);
|
|
static qse_awk_val_t** get_reference_indexed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* nde, qse_awk_val_t** val);
|
|
|
|
static qse_awk_val_t* eval_int (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_flt (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_str (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_mbs (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_rex (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_fun (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_named (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_gbl (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_lcl (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_arg (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_namedidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_gblidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_lclidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_argidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_pos (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_getline (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_print (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
static qse_awk_val_t* eval_printf (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
static int __raw_push (qse_awk_rtx_t* rtx, void* val);
|
|
#define __raw_pop(run) \
|
|
do { \
|
|
QSE_ASSERT ((run)->stack_top > (run)->stack_base); \
|
|
(run)->stack_top--; \
|
|
} while (0)
|
|
|
|
static int read_record (qse_awk_rtx_t* rtx);
|
|
|
|
static qse_char_t* idxnde_to_str (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, qse_char_t* buf, qse_size_t* len);
|
|
|
|
typedef qse_awk_val_t* (*binop_func_t) (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right);
|
|
typedef qse_awk_val_t* (*eval_expr_t) (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde);
|
|
|
|
|
|
QSE_INLINE qse_size_t qse_awk_rtx_getnargs (qse_awk_rtx_t* rtx)
|
|
{
|
|
return (qse_size_t) RTX_STACK_NARGS(rtx);
|
|
}
|
|
|
|
QSE_INLINE qse_awk_val_t* qse_awk_rtx_getarg (qse_awk_rtx_t* rtx, qse_size_t idx)
|
|
{
|
|
return RTX_STACK_ARG(rtx, idx);
|
|
}
|
|
|
|
QSE_INLINE qse_awk_val_t* qse_awk_rtx_getgbl (qse_awk_rtx_t* rtx, int id)
|
|
{
|
|
QSE_ASSERT (id >= 0 && id < (int)QSE_ARR_SIZE(rtx->awk->parse.gbls));
|
|
return RTX_STACK_GBL(rtx, id);
|
|
}
|
|
|
|
const qse_cstr_t* qse_awk_rtx_getsubsep (qse_awk_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 (qse_awk_rtx_t* rtx, int idx, qse_awk_nde_var_t* var, qse_awk_val_t* val, int assign)
|
|
{
|
|
qse_awk_val_t* old;
|
|
qse_awk_rtx_ecb_t* ecb;
|
|
qse_awk_val_type_t vtype, old_vtype;
|
|
|
|
old = RTX_STACK_GBL (rtx, idx);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, val);
|
|
old_vtype = QSE_AWK_RTX_GETVALTYPE (rtx, old);
|
|
|
|
if (!(rtx->awk->opt.trait & QSE_AWK_FLEXMAP))
|
|
{
|
|
qse_awk_errnum_t errnum = QSE_AWK_ENOERR;
|
|
|
|
if (vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
if (old_vtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
/* a nil valul can be overridden with any values */
|
|
/* ok. no error */
|
|
}
|
|
else if (!assign && old_vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* 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 = QSE_AWK_ENSCALARTOMAP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (old_vtype == QSE_AWK_VAL_MAP) errnum = QSE_AWK_ENMAPTOSCALAR;
|
|
}
|
|
|
|
if (errnum != QSE_AWK_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 */
|
|
SETERR_ARGX_LOC (rtx, errnum, &var->id.name, &var->loc);
|
|
}
|
|
else
|
|
{
|
|
/* qse_awk_rtx_setgbl() has been called */
|
|
qse_cstr_t ea;
|
|
ea.ptr = (qse_char_t*)qse_awk_getgblname (rtx->awk, idx, &ea.len);
|
|
SETERR_ARGX (rtx, errnum, &ea);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
if (idx >= QSE_AWK_MIN_GBL_ID && idx <= QSE_AWK_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???? */
|
|
|
|
qse_cstr_t ea;
|
|
ea.ptr = (qse_char_t*)qse_awk_getgblname (rtx->awk, idx, &ea.len);
|
|
SETERR_ARGX (rtx, QSE_AWK_ENSCALARTOMAP, &ea);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (old == val && idx != QSE_AWK_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 QSE_AWK_GBL_CONVFMT:
|
|
{
|
|
qse_size_t i;
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr (rtx, val, &out) <= -1)
|
|
return -1;
|
|
|
|
for (i = 0; i < out.u.cpldup.len; i++)
|
|
{
|
|
if (out.u.cpldup.ptr[i] == QSE_T('\0'))
|
|
{
|
|
/* '\0' is included in the value */
|
|
qse_awk_rtx_freemem (rtx, out.u.cpldup.ptr);
|
|
SETERR_COD (rtx, QSE_AWK_ECONVFMTCHR);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (rtx->gbl.convfmt.ptr) qse_awk_rtx_freemem (rtx, rtx->gbl.convfmt.ptr);
|
|
rtx->gbl.convfmt.ptr = out.u.cpldup.ptr;
|
|
rtx->gbl.convfmt.len = out.u.cpldup.len;
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_FNR:
|
|
{
|
|
int n;
|
|
qse_awk_int_t lv;
|
|
|
|
n = qse_awk_rtx_valtoint (rtx, val, &lv);
|
|
if (n <= -1) return -1;
|
|
|
|
rtx->gbl.fnr = lv;
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_FS:
|
|
{
|
|
qse_char_t* fs_ptr;
|
|
qse_size_t fs_len;
|
|
|
|
if (vtype == QSE_AWK_VAL_STR)
|
|
{
|
|
fs_ptr = ((qse_awk_val_str_t*)val)->val.ptr;
|
|
fs_len = ((qse_awk_val_str_t*)val)->val.len;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
/* due to the expression evaluation rule, the
|
|
* regular expression can not be an assigned value */
|
|
QSE_ASSERT (vtype != QSE_AWK_VAL_REX);
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr (rtx, val, &out) <= -1) return -1;
|
|
fs_ptr = out.u.cpldup.ptr;
|
|
fs_len = out.u.cpldup.len;
|
|
}
|
|
|
|
if (fs_len > 1 && !(fs_len == 5 && fs_ptr[0] == QSE_T('?')))
|
|
{
|
|
/* 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. */
|
|
void* rex, * irex;
|
|
qse_awk_errnum_t errnum;
|
|
|
|
if (qse_awk_buildrex (rtx->awk, fs_ptr, fs_len, &errnum, &rex, &irex) <= -1)
|
|
{
|
|
SETERR_COD (rtx, errnum);
|
|
if (vtype != QSE_AWK_VAL_STR)
|
|
qse_awk_rtx_freemem (rtx, fs_ptr);
|
|
return -1;
|
|
}
|
|
|
|
if (rtx->gbl.fs[0]) qse_awk_freerex (rtx->awk, rtx->gbl.fs[0], rtx->gbl.fs[1]);
|
|
|
|
rtx->gbl.fs[0] = rex;
|
|
rtx->gbl.fs[1] = irex;
|
|
}
|
|
|
|
if (vtype != QSE_AWK_VAL_STR) qse_awk_rtx_freemem (rtx, fs_ptr);
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_IGNORECASE:
|
|
{
|
|
qse_awk_int_t l;
|
|
qse_awk_flt_t r;
|
|
int vt;
|
|
|
|
vt = qse_awk_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 QSE_AWK_GBL_NF:
|
|
{
|
|
int n;
|
|
qse_awk_int_t lv;
|
|
|
|
n = qse_awk_rtx_valtoint(rtx, val, &lv);
|
|
if (n <= -1) return -1;
|
|
|
|
if (lv < 0)
|
|
{
|
|
qse_awk_rtx_seterrfmt (rtx, QSE_AWK_EINVAL, QSE_NULL, QSE_T("negative value into NF"));
|
|
return -1;
|
|
}
|
|
|
|
if (lv < (qse_awk_int_t)rtx->inrec.nflds || (assign && lv == (qse_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 qse_awk_rtx_setgbl().
|
|
*/
|
|
if (qse_awk_rtx_truncrec(rtx, (qse_size_t)lv) == -1)
|
|
{
|
|
/* adjust the error line */
|
|
/*if (var) ADJERR_LOC (rtx, &var->loc);*/
|
|
return -1;
|
|
}
|
|
}
|
|
else if (lv > (qse_awk_int_t)rtx->inrec.nflds)
|
|
{
|
|
qse_cstr_t cs;
|
|
cs.ptr = QSE_T("");
|
|
cs.len = 0;
|
|
if (qse_awk_rtx_setrec(rtx, lv, &cs) <= -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/code block */
|
|
if (old == val) return 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_NR:
|
|
{
|
|
int n;
|
|
qse_awk_int_t lv;
|
|
|
|
n = qse_awk_rtx_valtoint(rtx, val, &lv);
|
|
if (n <= -1) return -1;
|
|
|
|
rtx->gbl.nr = lv;
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_OFMT:
|
|
{
|
|
qse_size_t i;
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
|
|
|
|
for (i = 0; i < out.u.cpldup.len; i++)
|
|
{
|
|
if (out.u.cpldup.ptr[i] == QSE_T('\0'))
|
|
{
|
|
qse_awk_rtx_freemem (rtx, out.u.cpldup.ptr);
|
|
SETERR_COD (rtx, QSE_AWK_EOFMTCHR);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (rtx->gbl.ofmt.ptr) qse_awk_rtx_freemem (rtx, rtx->gbl.ofmt.ptr);
|
|
rtx->gbl.ofmt.ptr = out.u.cpldup.ptr;
|
|
rtx->gbl.ofmt.len = out.u.cpldup.len;
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_OFS:
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
|
|
if (rtx->gbl.ofs.ptr) qse_awk_rtx_freemem (rtx, rtx->gbl.ofs.ptr);
|
|
rtx->gbl.ofs.ptr = out.u.cpldup.ptr;
|
|
rtx->gbl.ofs.len = out.u.cpldup.len;
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_ORS:
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
|
|
if (rtx->gbl.ors.ptr) qse_awk_rtx_freemem (rtx, rtx->gbl.ors.ptr);
|
|
rtx->gbl.ors.ptr = out.u.cpldup.ptr;
|
|
rtx->gbl.ors.len = out.u.cpldup.len;
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_RS:
|
|
{
|
|
qse_cstr_t rss;
|
|
|
|
if (vtype == QSE_AWK_VAL_STR)
|
|
{
|
|
rss = ((qse_awk_val_str_t*)val)->val;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
/* due to the expression evaluation rule, the
|
|
* regular expression can not be an assigned
|
|
* value */
|
|
QSE_ASSERT (vtype != QSE_AWK_VAL_REX);
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
|
|
|
|
rss = out.u.cpldup;
|
|
}
|
|
|
|
if (rtx->gbl.rs[0])
|
|
{
|
|
qse_awk_freerex (rtx->awk, rtx->gbl.rs[0], rtx->gbl.rs[1]);
|
|
rtx->gbl.rs[0] = QSE_NULL;
|
|
rtx->gbl.rs[1] = QSE_NULL;
|
|
}
|
|
|
|
if (rss.len > 1)
|
|
{
|
|
void* rex, * irex;
|
|
qse_awk_errnum_t errnum;
|
|
|
|
/* compile the regular expression */
|
|
if (qse_awk_buildrex(rtx->awk, rss.ptr, rss.len, &errnum, &rex, &irex) <= -1)
|
|
{
|
|
SETERR_COD (rtx, errnum);
|
|
if (vtype != QSE_AWK_VAL_STR) qse_awk_rtx_freemem (rtx, rss.ptr);
|
|
return -1;
|
|
}
|
|
|
|
rtx->gbl.rs[0] = rex;
|
|
rtx->gbl.rs[1] = irex;
|
|
}
|
|
|
|
if (vtype != QSE_AWK_VAL_STR) qse_awk_rtx_freemem (rtx, rss.ptr);
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_GBL_STRIPRECSPC:
|
|
{
|
|
qse_awk_int_t l;
|
|
qse_awk_flt_t r;
|
|
int vt;
|
|
|
|
vt = qse_awk_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 QSE_AWK_GBL_SUBSEP:
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
|
|
|
|
if (rtx->gbl.subsep.ptr) qse_awk_rtx_freemem (rtx, rtx->gbl.subsep.ptr);
|
|
rtx->gbl.subsep.ptr = out.u.cpldup.ptr;
|
|
rtx->gbl.subsep.len = out.u.cpldup.len;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, old);
|
|
RTX_STACK_GBL(rtx,idx) = val;
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
|
|
for (ecb = (rtx)->ecb; ecb; ecb = ecb->next)
|
|
{
|
|
if (ecb->gblset) ecb->gblset (rtx, idx, val);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QSE_INLINE void qse_awk_rtx_setretval (qse_awk_rtx_t* rtx, qse_awk_val_t* val)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_RETVAL(rtx));
|
|
RTX_STACK_RETVAL(rtx) = val;
|
|
/* should use the same trick as run_return */
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
}
|
|
|
|
QSE_INLINE int qse_awk_rtx_setgbl (qse_awk_rtx_t* rtx, int id, qse_awk_val_t* val)
|
|
{
|
|
QSE_ASSERT (id >= 0 && id < (int)QSE_ARR_SIZE(rtx->awk->parse.gbls));
|
|
return set_global (rtx, id, QSE_NULL, val, 0);
|
|
}
|
|
|
|
int qse_awk_rtx_setfilename (qse_awk_rtx_t* rtx, const qse_char_t* name, qse_size_t len)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
int n;
|
|
|
|
if (len == 0) tmp = qse_awk_val_zls;
|
|
else
|
|
{
|
|
tmp = qse_awk_rtx_makestrval (rtx, name, len);
|
|
if (tmp == QSE_NULL) return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
n = qse_awk_rtx_setgbl (rtx, QSE_AWK_GBL_FILENAME, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
|
|
return n;
|
|
}
|
|
|
|
int qse_awk_rtx_setofilename (qse_awk_rtx_t* rtx, const qse_char_t* name, qse_size_t len)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
int n;
|
|
|
|
if (rtx->awk->opt.trait & QSE_AWK_NEXTOFILE)
|
|
{
|
|
if (len == 0) tmp = qse_awk_val_zls;
|
|
else
|
|
{
|
|
tmp = qse_awk_rtx_makestrval (rtx, name, len);
|
|
if (tmp == QSE_NULL) return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
n = qse_awk_rtx_setgbl (rtx, QSE_AWK_GBL_OFILENAME, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
}
|
|
else n = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
qse_htb_t* qse_awk_rtx_getnvmap (qse_awk_rtx_t* rtx)
|
|
{
|
|
return rtx->named;
|
|
}
|
|
|
|
struct module_init_ctx_t
|
|
{
|
|
qse_size_t count;
|
|
qse_awk_rtx_t* rtx;
|
|
};
|
|
|
|
struct module_fini_ctx_t
|
|
{
|
|
qse_size_t limit;
|
|
qse_size_t count;
|
|
qse_awk_rtx_t* rtx;
|
|
};
|
|
|
|
static qse_rbt_walk_t init_module (qse_rbt_t* rbt, qse_rbt_pair_t* pair, void* ctx)
|
|
{
|
|
qse_awk_mod_data_t* md;
|
|
struct module_init_ctx_t* mic;
|
|
|
|
mic = (struct module_init_ctx_t*)ctx;
|
|
|
|
md = (qse_awk_mod_data_t*)QSE_RBT_VPTR(pair);
|
|
if (md->mod.init && md->mod.init (&md->mod, mic->rtx) <= -1)
|
|
return QSE_RBT_WALK_STOP;
|
|
|
|
mic->count++;
|
|
return QSE_RBT_WALK_FORWARD;
|
|
}
|
|
|
|
static qse_rbt_walk_t fini_module (qse_rbt_t* rbt, qse_rbt_pair_t* pair, void* ctx)
|
|
{
|
|
qse_awk_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 QSE_RBT_WALK_STOP;
|
|
|
|
md = (qse_awk_mod_data_t*)QSE_RBT_VPTR(pair);
|
|
if (md->mod.fini) md->mod.fini (&md->mod, mfc->rtx);
|
|
|
|
mfc->count++;
|
|
return QSE_RBT_WALK_FORWARD;
|
|
}
|
|
|
|
qse_awk_rtx_t* qse_awk_rtx_open (qse_awk_t* awk, qse_size_t xtnsize, qse_awk_rio_t* rio)
|
|
{
|
|
qse_awk_rtx_t* rtx;
|
|
struct module_init_ctx_t mic;
|
|
|
|
/* clear the awk error code */
|
|
qse_awk_seterrnum (awk, QSE_AWK_ENOERR, QSE_NULL);
|
|
|
|
/* check if the code has ever been parsed */
|
|
if (awk->tree.ngbls == 0 &&
|
|
awk->tree.begin == QSE_NULL &&
|
|
awk->tree.end == QSE_NULL &&
|
|
awk->tree.chain_size == 0 &&
|
|
qse_htb_getsize(awk->tree.funs) == 0)
|
|
{
|
|
qse_awk_seterrnum (awk, QSE_AWK_EPERM, QSE_NULL);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* allocate the storage for the rtx object */
|
|
rtx = (qse_awk_rtx_t*)qse_awk_allocmem(awk, QSE_SIZEOF(qse_awk_rtx_t) + xtnsize);
|
|
if (!rtx)
|
|
{
|
|
/* if it fails, the failure is reported thru the awk object */
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* initialize the rtx object */
|
|
QSE_MEMSET (rtx, 0, QSE_SIZEOF(qse_awk_rtx_t) + xtnsize);
|
|
rtx->_instsize = QSE_SIZEOF(qse_awk_rtx_t);
|
|
if (init_rtx(rtx, awk, rio) <= -1)
|
|
{
|
|
qse_awk_freemem (awk, rtx);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (init_globals(rtx) <= -1)
|
|
{
|
|
awk->errinf = rtx->errinf; /* transfer error info */
|
|
fini_rtx (rtx, 0);
|
|
qse_awk_freemem (awk, rtx);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
mic.count = 0;
|
|
mic.rtx = rtx;
|
|
qse_rbt_walk (rtx->awk->modtab, init_module, &mic);
|
|
if (mic.count != QSE_RBT_SIZE(rtx->awk->modtab))
|
|
{
|
|
awk->errinf = rtx->errinf; /* transfer error info */
|
|
|
|
if (mic.count > 0)
|
|
{
|
|
struct module_fini_ctx_t mfc;
|
|
mfc.limit = mic.count;
|
|
mfc.count = 0;
|
|
qse_rbt_walk (rtx->awk->modtab, fini_module, &mfc);
|
|
}
|
|
|
|
fini_rtx (rtx, 1);
|
|
qse_awk_freemem (awk, rtx);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return rtx;
|
|
}
|
|
|
|
void qse_awk_rtx_close (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_awk_rtx_ecb_t* ecb;
|
|
struct module_fini_ctx_t mfc;
|
|
|
|
mfc.limit = 0;
|
|
mfc.count = 0;
|
|
mfc.rtx = rtx;
|
|
qse_rbt_walk (rtx->awk->modtab, fini_module, &mfc);
|
|
|
|
for (ecb = rtx->ecb; ecb; ecb = ecb->next)
|
|
{
|
|
if (ecb->close) ecb->close (rtx);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
qse_awk_freemem (rtx->awk, rtx);
|
|
}
|
|
|
|
void qse_awk_rtx_halt (qse_awk_rtx_t* rtx)
|
|
{
|
|
rtx->exit_level = EXIT_ABORT;
|
|
}
|
|
|
|
int qse_awk_rtx_ishalt (qse_awk_rtx_t* rtx)
|
|
{
|
|
return (rtx->exit_level == EXIT_ABORT || rtx->awk->haltall);
|
|
}
|
|
|
|
void qse_awk_rtx_getrio (qse_awk_rtx_t* rtx, qse_awk_rio_t* rio)
|
|
{
|
|
rio->pipe = rtx->rio.handler[QSE_AWK_RIO_PIPE];
|
|
rio->file = rtx->rio.handler[QSE_AWK_RIO_FILE];
|
|
rio->console = rtx->rio.handler[QSE_AWK_RIO_CONSOLE];
|
|
}
|
|
|
|
void qse_awk_rtx_setrio (qse_awk_rtx_t* rtx, const qse_awk_rio_t* rio)
|
|
{
|
|
rtx->rio.handler[QSE_AWK_RIO_PIPE] = rio->pipe;
|
|
rtx->rio.handler[QSE_AWK_RIO_FILE] = rio->file;
|
|
rtx->rio.handler[QSE_AWK_RIO_CONSOLE] = rio->console;
|
|
}
|
|
|
|
qse_awk_rtx_ecb_t* qse_awk_rtx_popecb (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_awk_rtx_ecb_t* top = rtx->ecb;
|
|
if (top) rtx->ecb = top->next;
|
|
return top;
|
|
}
|
|
|
|
void qse_awk_rtx_pushecb (qse_awk_rtx_t* rtx, qse_awk_rtx_ecb_t* ecb)
|
|
{
|
|
ecb->next = rtx->ecb;
|
|
rtx->ecb = ecb;
|
|
}
|
|
|
|
static void free_namedval (qse_htb_t* map, void* dptr, qse_size_t dlen)
|
|
{
|
|
qse_awk_rtx_refdownval (*(qse_awk_rtx_t**)QSE_XTN(map), dptr);
|
|
}
|
|
|
|
static void same_namedval (qse_htb_t* map, void* dptr, qse_size_t dlen)
|
|
{
|
|
qse_awk_rtx_refdownval_nofree (*(qse_awk_rtx_t**)QSE_XTN(map), dptr);
|
|
}
|
|
|
|
static int init_rtx (qse_awk_rtx_t* rtx, qse_awk_t* awk, qse_awk_rio_t* rio)
|
|
{
|
|
static qse_htb_style_t style_for_named =
|
|
{
|
|
{
|
|
QSE_HTB_COPIER_INLINE,
|
|
QSE_HTB_COPIER_DEFAULT
|
|
},
|
|
{
|
|
QSE_HTB_FREEER_DEFAULT,
|
|
free_namedval
|
|
},
|
|
QSE_HTB_COMPER_DEFAULT,
|
|
same_namedval,
|
|
QSE_HTB_SIZER_DEFAULT,
|
|
QSE_HTB_HASHER_DEFAULT
|
|
};
|
|
qse_size_t stack_limit;
|
|
|
|
rtx->awk = awk;
|
|
|
|
CLRERR (rtx);
|
|
|
|
stack_limit = awk->parse.pragma.rtx_stack_limit > 0? awk->parse.pragma.rtx_stack_limit: awk->opt.rtx_stack_limit;
|
|
if (stack_limit < QSE_AWK_MIN_RTX_STACK_LIMIT) stack_limit = QSE_AWK_MIN_RTX_STACK_LIMIT;
|
|
rtx->stack = qse_awk_rtx_allocmem(rtx, stack_limit * QSE_SIZEOF(void*));
|
|
if (!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 = QSE_NULL;
|
|
rtx->vmgr.ifree = QSE_NULL;
|
|
rtx->vmgr.rchunk = QSE_NULL;
|
|
rtx->vmgr.rfree = QSE_NULL;
|
|
|
|
rtx->inrec.buf_pos = 0;
|
|
rtx->inrec.buf_len = 0;
|
|
rtx->inrec.flds = QSE_NULL;
|
|
rtx->inrec.nflds = 0;
|
|
rtx->inrec.maxflds = 0;
|
|
rtx->inrec.d0 = qse_awk_val_nil;
|
|
|
|
if (qse_str_init(&rtx->inrec.line, qse_awk_rtx_getmmgr(rtx), DEF_BUF_CAPA) <= -1) goto oops_1;
|
|
if (qse_str_init(&rtx->inrec.linew, qse_awk_rtx_getmmgr(rtx), DEF_BUF_CAPA) <= -1) goto oops_2;
|
|
if (qse_str_init(&rtx->inrec.lineg, qse_awk_rtx_getmmgr(rtx), DEF_BUF_CAPA) <= -1) goto oops_3;
|
|
if (qse_str_init(&rtx->format.out, qse_awk_rtx_getmmgr(rtx), 256) <= -1) goto oops_4;
|
|
if (qse_str_init(&rtx->format.fmt, qse_awk_rtx_getmmgr(rtx), 256) <= -1) goto oops_5;
|
|
|
|
if (qse_mbs_init(&rtx->formatmbs.out, qse_awk_rtx_getmmgr(rtx), 256) <= -1) goto oops_6;
|
|
if (qse_mbs_init(&rtx->formatmbs.fmt, qse_awk_rtx_getmmgr(rtx), 256) <= -1) goto oops_7;
|
|
|
|
rtx->named = qse_htb_open(qse_awk_rtx_getmmgr(rtx), QSE_SIZEOF(rtx), 1024, 70, QSE_SIZEOF(qse_char_t), 1);
|
|
if (!rtx->named) goto oops_8;
|
|
*(qse_awk_rtx_t**)QSE_XTN(rtx->named) = rtx;
|
|
qse_htb_setstyle (rtx->named, &style_for_named);
|
|
|
|
rtx->format.tmp.ptr = (qse_char_t*)qse_awk_rtx_allocmem(rtx, 4096 * QSE_SIZEOF(qse_char_t));
|
|
if (!rtx->format.tmp.ptr) goto oops_9; /* the error is set on the awk object after this jump is made */
|
|
rtx->format.tmp.len = 4096;
|
|
rtx->format.tmp.inc = 4096 * 2;
|
|
|
|
rtx->formatmbs.tmp.ptr = (qse_mchar_t*)qse_awk_rtx_allocmem(rtx, 4096 * QSE_SIZEOF(qse_mchar_t));
|
|
if (!rtx->formatmbs.tmp.ptr) goto oops_10;
|
|
rtx->formatmbs.tmp.len = 4096;
|
|
rtx->formatmbs.tmp.inc = 4096 * 2;
|
|
|
|
if (rtx->awk->tree.chain_size > 0)
|
|
{
|
|
rtx->pattern_range_state = (qse_byte_t*)qse_awk_rtx_allocmem(rtx, rtx->awk->tree.chain_size * QSE_SIZEOF(qse_byte_t));
|
|
if (!rtx->pattern_range_state) goto oops_11;
|
|
QSE_MEMSET (rtx->pattern_range_state, 0, rtx->awk->tree.chain_size * QSE_SIZEOF(qse_byte_t));
|
|
}
|
|
else rtx->pattern_range_state = QSE_NULL;
|
|
|
|
if (rio)
|
|
{
|
|
rtx->rio.handler[QSE_AWK_RIO_PIPE] = rio->pipe;
|
|
rtx->rio.handler[QSE_AWK_RIO_FILE] = rio->file;
|
|
rtx->rio.handler[QSE_AWK_RIO_CONSOLE] = rio->console;
|
|
rtx->rio.chain = QSE_NULL;
|
|
}
|
|
|
|
rtx->gbl.rs[0] = QSE_NULL;
|
|
rtx->gbl.rs[1] = QSE_NULL;
|
|
rtx->gbl.fs[0] = QSE_NULL;
|
|
rtx->gbl.fs[1] = QSE_NULL;
|
|
rtx->gbl.ignorecase = 0;
|
|
rtx->gbl.striprecspc = -1;
|
|
|
|
return 0;
|
|
|
|
oops_11:
|
|
qse_awk_rtx_freemem (rtx, rtx->formatmbs.tmp.ptr);
|
|
oops_10:
|
|
qse_awk_rtx_freemem (rtx, rtx->format.tmp.ptr);
|
|
oops_9:
|
|
qse_htb_close (rtx->named);
|
|
oops_8:
|
|
qse_mbs_fini (&rtx->formatmbs.fmt);
|
|
oops_7:
|
|
qse_mbs_fini (&rtx->formatmbs.out);
|
|
oops_6:
|
|
qse_str_fini (&rtx->format.fmt);
|
|
oops_5:
|
|
qse_str_fini (&rtx->format.out);
|
|
oops_4:
|
|
qse_str_fini (&rtx->inrec.lineg);
|
|
oops_3:
|
|
qse_str_fini (&rtx->inrec.linew);
|
|
oops_2:
|
|
qse_str_fini (&rtx->inrec.line);
|
|
oops_1:
|
|
qse_awk_rtx_freemem (rtx, rtx->stack);
|
|
oops_0:
|
|
qse_awk_seterrnum (awk, QSE_AWK_ENOMEM, QSE_NULL);
|
|
return -1;
|
|
}
|
|
|
|
static void fini_rtx (qse_awk_rtx_t* rtx, int fini_globals)
|
|
{
|
|
if (rtx->pattern_range_state)
|
|
qse_awk_rtx_freemem (rtx, rtx->pattern_range_state);
|
|
|
|
/* close all pending io's */
|
|
/* TODO: what if this operation fails? */
|
|
qse_awk_rtx_cleario (rtx);
|
|
QSE_ASSERT (rtx->rio.chain == QSE_NULL);
|
|
|
|
if (rtx->gbl.rs[0])
|
|
{
|
|
qse_awk_freerex (rtx->awk, rtx->gbl.rs[0], rtx->gbl.rs[1]);
|
|
rtx->gbl.rs[0] = QSE_NULL;
|
|
rtx->gbl.rs[1] = QSE_NULL;
|
|
}
|
|
if (rtx->gbl.fs[0])
|
|
{
|
|
qse_awk_freerex (rtx->awk, rtx->gbl.fs[0], rtx->gbl.fs[1]);
|
|
rtx->gbl.fs[0] = QSE_NULL;
|
|
rtx->gbl.fs[1] = QSE_NULL;
|
|
}
|
|
|
|
if (rtx->gbl.convfmt.ptr != QSE_NULL &&
|
|
rtx->gbl.convfmt.ptr != DEFAULT_CONVFMT)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->gbl.convfmt.ptr);
|
|
rtx->gbl.convfmt.ptr = QSE_NULL;
|
|
rtx->gbl.convfmt.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ofmt.ptr != QSE_NULL &&
|
|
rtx->gbl.ofmt.ptr != DEFAULT_OFMT)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->gbl.ofmt.ptr);
|
|
rtx->gbl.ofmt.ptr = QSE_NULL;
|
|
rtx->gbl.ofmt.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ofs.ptr != QSE_NULL &&
|
|
rtx->gbl.ofs.ptr != DEFAULT_OFS)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->gbl.ofs.ptr);
|
|
rtx->gbl.ofs.ptr = QSE_NULL;
|
|
rtx->gbl.ofs.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ors.ptr != QSE_NULL &&
|
|
rtx->gbl.ors.ptr != DEFAULT_ORS &&
|
|
rtx->gbl.ors.ptr != DEFAULT_ORS_CRLF)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->gbl.ors.ptr);
|
|
rtx->gbl.ors.ptr = QSE_NULL;
|
|
rtx->gbl.ors.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.subsep.ptr != QSE_NULL &&
|
|
rtx->gbl.subsep.ptr != DEFAULT_SUBSEP)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->gbl.subsep.ptr);
|
|
rtx->gbl.subsep.ptr = QSE_NULL;
|
|
rtx->gbl.subsep.len = 0;
|
|
}
|
|
|
|
qse_awk_rtx_freemem (rtx, rtx->formatmbs.tmp.ptr);
|
|
rtx->formatmbs.tmp.ptr = QSE_NULL;
|
|
rtx->formatmbs.tmp.len = 0;
|
|
qse_mbs_fini (&rtx->formatmbs.fmt);
|
|
qse_mbs_fini (&rtx->formatmbs.out);
|
|
|
|
qse_awk_rtx_freemem (rtx, rtx->format.tmp.ptr);
|
|
rtx->format.tmp.ptr = QSE_NULL;
|
|
rtx->format.tmp.len = 0;
|
|
qse_str_fini (&rtx->format.fmt);
|
|
qse_str_fini (&rtx->format.out);
|
|
|
|
/* destroy input record. qse_awk_rtx_clrrec() should be called
|
|
* before the stack has been destroyed because it may try
|
|
* to change the value to QSE_AWK_GBL_NF. */
|
|
qse_awk_rtx_clrrec (rtx, 0);
|
|
if (rtx->inrec.flds)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, rtx->inrec.flds);
|
|
rtx->inrec.flds = QSE_NULL;
|
|
rtx->inrec.maxflds = 0;
|
|
}
|
|
qse_str_fini (&rtx->inrec.lineg);
|
|
qse_str_fini (&rtx->inrec.linew);
|
|
qse_str_fini (&rtx->inrec.line);
|
|
|
|
if (fini_globals) refdown_globals (rtx, 1);
|
|
|
|
/* destroy the stack if necessary */
|
|
if (rtx->stack)
|
|
{
|
|
QSE_ASSERT (rtx->stack_top == 0);
|
|
|
|
qse_awk_rtx_freemem (rtx, rtx->stack);
|
|
rtx->stack = QSE_NULL;
|
|
rtx->stack_top = 0;
|
|
rtx->stack_base = 0;
|
|
rtx->stack_limit = 0;
|
|
}
|
|
|
|
/* destroy named variables */
|
|
qse_htb_close (rtx->named);
|
|
|
|
/* destroy values in free list */
|
|
while (rtx->rcache_count > 0)
|
|
{
|
|
qse_awk_val_ref_t* tmp = rtx->rcache[--rtx->rcache_count];
|
|
qse_awk_rtx_freeval (rtx, (qse_awk_val_t*)tmp, 0);
|
|
}
|
|
|
|
#ifdef ENABLE_FEATURE_SCACHE
|
|
{
|
|
int i;
|
|
for (i = 0; i < QSE_COUNTOF(rtx->scache_count); i++)
|
|
{
|
|
while (rtx->scache_count[i] > 0)
|
|
{
|
|
qse_awk_val_str_t* t = rtx->scache[i][--rtx->scache_count[i]];
|
|
qse_awk_rtx_freeval (rtx, (qse_awk_val_t*)t, 0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
qse_awk_rtx_freevalchunk (rtx, rtx->vmgr.ichunk);
|
|
qse_awk_rtx_freevalchunk (rtx, rtx->vmgr.rchunk);
|
|
rtx->vmgr.ichunk = QSE_NULL;
|
|
rtx->vmgr.rchunk = QSE_NULL;
|
|
}
|
|
|
|
static int update_fnr (qse_awk_rtx_t* rtx, qse_awk_int_t fnr, qse_awk_int_t nr)
|
|
{
|
|
qse_awk_val_t* tmp1, * tmp2;
|
|
|
|
tmp1 = qse_awk_rtx_makeintval(rtx, fnr);
|
|
if (!tmp1) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp1);
|
|
|
|
if (nr == fnr) tmp2 = tmp1;
|
|
else
|
|
{
|
|
tmp2 = qse_awk_rtx_makeintval(rtx, nr);
|
|
if (!tmp2)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, tmp1);
|
|
return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp2);
|
|
}
|
|
|
|
if (qse_awk_rtx_setgbl (rtx, QSE_AWK_GBL_FNR, tmp1) == -1)
|
|
{
|
|
if (nr != fnr) qse_awk_rtx_refdownval (rtx, tmp2);
|
|
qse_awk_rtx_refdownval (rtx, tmp1);
|
|
return -1;
|
|
}
|
|
|
|
if (qse_awk_rtx_setgbl (rtx, QSE_AWK_GBL_NR, tmp2) == -1)
|
|
{
|
|
if (nr != fnr) qse_awk_rtx_refdownval (rtx, tmp2);
|
|
qse_awk_rtx_refdownval (rtx, tmp1);
|
|
return -1;
|
|
}
|
|
|
|
if (nr != fnr) qse_awk_rtx_refdownval (rtx, tmp2);
|
|
qse_awk_rtx_refdownval (rtx, tmp1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* create global variables into the runtime stack
|
|
* each variable is initialized to nil or zero.
|
|
*/
|
|
static int prepare_globals (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_size_t saved_stack_top;
|
|
qse_size_t ngbls;
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
ngbls = rtx->awk->tree.ngbls;
|
|
|
|
/* initialize all global variables to nil by push nils to the stack */
|
|
while (ngbls > 0)
|
|
{
|
|
--ngbls;
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
/* override NF to zero */
|
|
if (qse_awk_rtx_setgbl(rtx, QSE_AWK_GBL_NF, QSE_AWK_VAL_ZERO) <= -1) goto oops;
|
|
|
|
/* return success */
|
|
return 0;
|
|
|
|
oops:
|
|
/* restore the stack_top this way instead of calling __raw_pop()
|
|
* as many times as successful __raw_push(). it is ok because
|
|
* the values pushed so far are qse_awk_val_nils and QSE_AWK_VAL_ZEROs.
|
|
*/
|
|
rtx->stack_top = saved_stack_top;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* 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 (qse_awk_rtx_t* rtx)
|
|
{
|
|
struct gtab_t
|
|
{
|
|
int idx;
|
|
const qse_char_t* str[2];
|
|
};
|
|
static struct gtab_t gtab[8] =
|
|
{
|
|
{ QSE_AWK_GBL_CONVFMT, { DEFAULT_CONVFMT, DEFAULT_CONVFMT } },
|
|
{ QSE_AWK_GBL_FILENAME, { QSE_NULL, QSE_NULL } },
|
|
{ QSE_AWK_GBL_FS, { DEFAULT_FS, DEFAULT_FS } },
|
|
{ QSE_AWK_GBL_OFILENAME, { QSE_NULL, QSE_NULL } },
|
|
{ QSE_AWK_GBL_OFMT, { DEFAULT_OFMT, DEFAULT_OFMT } },
|
|
{ QSE_AWK_GBL_OFS, { DEFAULT_OFS, DEFAULT_OFS } },
|
|
{ QSE_AWK_GBL_ORS, { DEFAULT_ORS, DEFAULT_ORS_CRLF } },
|
|
{ QSE_AWK_GBL_SUBSEP, { DEFAULT_SUBSEP, DEFAULT_SUBSEP } },
|
|
};
|
|
|
|
qse_awk_val_t* tmp;
|
|
qse_size_t i, j;
|
|
int stridx;
|
|
|
|
stridx = (rtx->awk->opt.trait & QSE_AWK_CRLF)? 1: 0;
|
|
for (i = 0; i < QSE_COUNTOF(gtab); i++)
|
|
{
|
|
if (gtab[i].str[stridx] == QSE_NULL || gtab[i].str[stridx][0] == QSE_T('\0'))
|
|
{
|
|
tmp = qse_awk_val_zls;
|
|
}
|
|
else
|
|
{
|
|
tmp = qse_awk_rtx_makestrvalwithstr (rtx, gtab[i].str[stridx]);
|
|
if (tmp == QSE_NULL) return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
|
|
QSE_ASSERT (RTX_STACK_GBL(rtx,gtab[i].idx) == qse_awk_val_nil);
|
|
|
|
if (qse_awk_rtx_setgbl(rtx, gtab[i].idx, tmp) == -1)
|
|
{
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
qse_awk_rtx_setgbl (rtx, gtab[i].idx, qse_awk_val_nil);
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void refdown_globals (qse_awk_rtx_t* run, int pop)
|
|
{
|
|
qse_size_t ngbls;
|
|
|
|
ngbls = run->awk->tree.ngbls;
|
|
while (ngbls > 0)
|
|
{
|
|
--ngbls;
|
|
qse_awk_rtx_refdownval (run, RTX_STACK_GBL(run,ngbls));
|
|
if (pop) __raw_pop (run);
|
|
else RTX_STACK_GBL(run,ngbls) = qse_awk_val_nil;
|
|
}
|
|
}
|
|
|
|
static int init_globals (qse_awk_rtx_t* rtx)
|
|
{
|
|
/* the stack must be clean when this function is invoked */
|
|
QSE_ASSERTX (rtx->stack_base == 0, "stack not clean");
|
|
QSE_ASSERTX (rtx->stack_top == 0, "stack not clean");
|
|
|
|
if (prepare_globals (rtx) == -1) return -1;
|
|
if (update_fnr (rtx, 0, 0) == -1) goto oops;
|
|
if (defaultify_globals (rtx) == -1) goto oops;
|
|
return 0;
|
|
|
|
oops:
|
|
refdown_globals (rtx, 1);
|
|
return -1;
|
|
}
|
|
|
|
struct capture_retval_data_t
|
|
{
|
|
qse_awk_rtx_t* rtx;
|
|
qse_awk_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 = RTX_STACK_RETVAL(data->rtx);
|
|
qse_awk_rtx_refupval (data->rtx, data->val);
|
|
}
|
|
|
|
static int enter_stack_frame (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_size_t saved_stack_top;
|
|
|
|
/* remember the current stack top */
|
|
saved_stack_top = rtx->stack_top;
|
|
|
|
/* push the current stack base */
|
|
if (__raw_push(rtx,(void*)rtx->stack_base) <= -1) goto oops;
|
|
|
|
/* push the current stack top before push the current stack base */
|
|
if (__raw_push(rtx,(void*)saved_stack_top) <= -1) goto oops;
|
|
|
|
/* secure space for a return value */
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1) goto oops;
|
|
|
|
/* secure space for RTX_STACK_NARGS */
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1) goto oops;
|
|
|
|
/* let the stack top remembered be the base of a new stack frame */
|
|
rtx->stack_base = saved_stack_top;
|
|
return 0;
|
|
|
|
oops:
|
|
/* restore the stack top in a cheesy(?) way.
|
|
* it is ok to do so as the values pushed are
|
|
* nils and binary numbers. */
|
|
rtx->stack_top = saved_stack_top;
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return -1;
|
|
}
|
|
|
|
static void exit_stack_frame (qse_awk_rtx_t* run)
|
|
{
|
|
/* At this point, the current stack frame should have
|
|
* the 4 entries pushed in enter_stack_frame(). */
|
|
QSE_ASSERT ((run->stack_top-run->stack_base) == 4);
|
|
|
|
run->stack_top = (qse_size_t)run->stack[run->stack_base + 1];
|
|
run->stack_base = (qse_size_t)run->stack[run->stack_base + 0];
|
|
}
|
|
|
|
static qse_awk_val_t* run_bpae_loop (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_awk_nde_t* nde;
|
|
qse_size_t nargs, i;
|
|
qse_awk_val_t* retv;
|
|
int ret = 0;
|
|
|
|
/* set nargs to zero */
|
|
nargs = 0;
|
|
RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
/* execute the BEGIN block */
|
|
for (nde = rtx->awk->tree.begin;
|
|
ret == 0 && nde != QSE_NULL && rtx->exit_level < EXIT_GLOBAL;
|
|
nde = nde->next)
|
|
{
|
|
qse_awk_nde_blk_t* blk;
|
|
|
|
blk = (qse_awk_nde_blk_t*)nde;
|
|
QSE_ASSERT (blk->type == QSE_AWK_NDE_BLK);
|
|
|
|
rtx->active_block = blk;
|
|
rtx->exit_level = EXIT_NONE;
|
|
if (run_block (rtx, blk) == -1) ret = -1;
|
|
}
|
|
|
|
if (ret <= -1 && rtx->errinf.num == QSE_AWK_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 */
|
|
}
|
|
|
|
/* run pattern block loops */
|
|
if (ret == 0 &&
|
|
(rtx->awk->tree.chain != QSE_NULL ||
|
|
rtx->awk->tree.end != QSE_NULL) &&
|
|
rtx->exit_level < EXIT_GLOBAL)
|
|
{
|
|
if (run_pblocks(rtx) <= -1) ret = -1;
|
|
}
|
|
|
|
if (ret <= -1 && rtx->errinf.num == QSE_AWK_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 qse_awk_rtx_halt().*/
|
|
for (nde = rtx->awk->tree.end;
|
|
ret == 0 && nde != QSE_NULL && rtx->exit_level < EXIT_ABORT;
|
|
nde = nde->next)
|
|
{
|
|
qse_awk_nde_blk_t* blk;
|
|
|
|
blk = (qse_awk_nde_blk_t*)nde;
|
|
QSE_ASSERT (blk->type == QSE_AWK_NDE_BLK);
|
|
|
|
rtx->active_block = blk;
|
|
rtx->exit_level = EXIT_NONE;
|
|
if (run_block (rtx, blk) <= -1) ret = -1;
|
|
else if (rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* once exit is called inside one of END blocks,
|
|
* subsequent END blocks must not be executed */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret <= -1 && rtx->errinf.num == QSE_AWK_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 */
|
|
}
|
|
|
|
/* 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 = (qse_size_t)RTX_STACK_NARGS(rtx);
|
|
QSE_ASSERT (nargs == 0);
|
|
for (i = 0; i < nargs; i++)
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
|
|
|
|
/* get the return value in the current stack frame */
|
|
retv = RTX_STACK_RETVAL(rtx);
|
|
|
|
if (ret <= -1)
|
|
{
|
|
/* end the life of the global return value upon error */
|
|
qse_awk_rtx_refdownval (rtx, retv);
|
|
retv = QSE_NULL;
|
|
}
|
|
|
|
return retv;
|
|
}
|
|
|
|
/* start the BEGIN-pattern block-END loop */
|
|
qse_awk_val_t* qse_awk_rtx_loop (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_awk_val_t* retv = QSE_NULL;
|
|
|
|
rtx->exit_level = EXIT_NONE;
|
|
|
|
if (enter_stack_frame (rtx) == 0)
|
|
{
|
|
retv = run_bpae_loop (rtx);
|
|
exit_stack_frame (rtx);
|
|
}
|
|
|
|
/* reset the exit level */
|
|
rtx->exit_level = EXIT_NONE;
|
|
return retv;
|
|
}
|
|
|
|
/* find an AWK function by name */
|
|
static qse_awk_fun_t* find_fun (qse_awk_rtx_t* rtx, const qse_char_t* name)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
pair = qse_htb_search(rtx->awk->tree.funs, name, qse_strlen(name));
|
|
|
|
if (!pair)
|
|
{
|
|
qse_cstr_t nm;
|
|
|
|
nm.ptr = (qse_char_t*)name;
|
|
nm.len = qse_strlen(name);
|
|
|
|
SETERR_ARGX (rtx, QSE_AWK_EFUNNF, &nm);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return (qse_awk_fun_t*)QSE_HTB_VPTR(pair);
|
|
}
|
|
|
|
qse_awk_fun_t* qse_awk_rtx_findfunwithmbs (qse_awk_rtx_t* rtx, const qse_mchar_t* name)
|
|
{
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
return find_fun(rtx, name);
|
|
#else
|
|
qse_wcstr_t wcs;
|
|
qse_awk_fun_t* fun;
|
|
wcs.ptr = qse_awk_rtx_mbstowcsdup(rtx, name, &wcs.len);
|
|
if (!wcs.ptr) return QSE_NULL;
|
|
fun = find_fun(rtx, wcs.ptr);
|
|
qse_awk_rtx_freemem (rtx, wcs.ptr);
|
|
return fun;
|
|
#endif
|
|
}
|
|
|
|
qse_awk_fun_t* qse_awk_rtx_findfunwithwcs (qse_awk_rtx_t* rtx, const qse_wchar_t* name)
|
|
{
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
qse_mcstr_t mbs;
|
|
qse_awk_fun_t* fun;
|
|
mbs.ptr = qse_awk_rtx_wcstombsdup(rtx, name, &mbs.len);
|
|
if (!mbs.ptr) return QSE_NULL;
|
|
fun = find_fun(rtx, mbs.ptr);
|
|
qse_awk_rtx_freemem (rtx, mbs.ptr);
|
|
return fun;
|
|
#else
|
|
return find_fun(rtx, name);
|
|
#endif
|
|
}
|
|
|
|
/* call an AWK function by the function structure */
|
|
qse_awk_val_t* qse_awk_rtx_callfun (qse_awk_rtx_t* rtx, qse_awk_fun_t* fun, qse_awk_val_t* args[], qse_size_t nargs)
|
|
{
|
|
struct capture_retval_data_t crdata;
|
|
qse_awk_val_t* v;
|
|
struct pafv_t pafv/*= { args, nargs }*/;
|
|
qse_awk_nde_fncall_t call;
|
|
|
|
QSE_ASSERT (fun != QSE_NULL);
|
|
|
|
pafv.args = args;
|
|
pafv.nargs = nargs;
|
|
pafv.argspec = fun->argspec;
|
|
|
|
if (rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* cannot call the function again when exit() is called
|
|
* in an AWK program or qse_awk_rtx_halt() is invoked */
|
|
SETERR_COD (rtx, QSE_AWK_EPERM);
|
|
return QSE_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 */
|
|
qse_awk_rtx_seterrfmt (rtx, QSE_AWK_EPERM, QSE_NULL, QSE_T("not allowed to call '%.*js' with pass-by-reference parameters"), (int)fun->name.len, fun->name.ptr);
|
|
return QSE_NULL;
|
|
}
|
|
#endif
|
|
|
|
/* forge a fake node containing a function call */
|
|
QSE_MEMSET (&call, 0, QSE_SIZEOF(call));
|
|
call.type = QSE_AWK_NDE_FNCALL_FUN;
|
|
call.u.fun.name = fun->name;
|
|
call.nargs = nargs;
|
|
/* keep QSE_NULL in call.args so that __eval_call() knows it's a fake call structure */
|
|
|
|
/* check if the number of arguments given is more than expected */
|
|
if (nargs > fun->nargs)
|
|
{
|
|
/* TODO: is this correct? what if i want to
|
|
* allow arbitrary numbers of arguments? */
|
|
SETERR_COD (rtx, QSE_AWK_EARGTM);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* now that the function is found and ok, let's execute it */
|
|
|
|
crdata.rtx = rtx;
|
|
crdata.val = QSE_NULL;
|
|
|
|
v = __eval_call(rtx, (qse_awk_nde_t*)&call, fun, push_arg_from_vals, (void*)&pafv, capture_retval_on_exit, &crdata);
|
|
|
|
if (!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. */
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
}
|
|
|
|
/* 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 */
|
|
qse_awk_val_t* qse_awk_rtx_callwithmbs (qse_awk_rtx_t* rtx, const qse_mchar_t* name, qse_awk_val_t* args[], qse_size_t nargs)
|
|
{
|
|
qse_awk_fun_t* fun;
|
|
|
|
fun = qse_awk_rtx_findfunwithmbs(rtx, name);
|
|
if (!fun) return QSE_NULL;
|
|
|
|
return qse_awk_rtx_callfun(rtx, fun, args, nargs);
|
|
}
|
|
|
|
qse_awk_val_t* qse_awk_rtx_callwithwcs (qse_awk_rtx_t* rtx, const qse_wchar_t* name, qse_awk_val_t* args[], qse_size_t nargs)
|
|
{
|
|
qse_awk_fun_t* fun;
|
|
|
|
fun = qse_awk_rtx_findfunwithwcs(rtx, name);
|
|
if (!fun) return QSE_NULL;
|
|
|
|
return qse_awk_rtx_callfun(rtx, fun, args, nargs);
|
|
}
|
|
|
|
qse_awk_val_t* qse_awk_rtx_callwithstrs (qse_awk_rtx_t* rtx, const qse_char_t* name, const qse_char_t* args[], qse_size_t nargs)
|
|
{
|
|
qse_size_t i;
|
|
qse_awk_val_t** v, * ret;
|
|
|
|
v = qse_awk_rtx_allocmem (rtx, QSE_SIZEOF(*v) * nargs);
|
|
if (!v) return QSE_NULL;
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
v[i] = qse_awk_rtx_makestrvalwithstr (rtx, args[i]);
|
|
if (!v[i])
|
|
{
|
|
ret = QSE_NULL;
|
|
goto oops;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v[i]);
|
|
}
|
|
|
|
ret = qse_awk_rtx_call(rtx, name, v, nargs);
|
|
|
|
oops:
|
|
while (i > 0)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v[--i]);
|
|
}
|
|
qse_awk_rtx_freemem (rtx, v);
|
|
return ret;
|
|
}
|
|
|
|
static int run_pblocks (qse_awk_rtx_t* rtx)
|
|
{
|
|
int n;
|
|
|
|
#define ADJUST_ERROR(run) \
|
|
if (rtx->awk->tree.chain != QSE_NULL) \
|
|
{ \
|
|
if (rtx->awk->tree.chain->pattern != QSE_NULL) \
|
|
ADJERR_LOC (rtx, &rtx->awk->tree.chain->pattern->loc); \
|
|
else if (rtx->awk->tree.chain->action != QSE_NULL) \
|
|
ADJERR_LOC (rtx, &rtx->awk->tree.chain->action->loc); \
|
|
} \
|
|
else if (rtx->awk->tree.end != QSE_NULL) \
|
|
{ \
|
|
ADJERR_LOC (run, &rtx->awk->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->awk->tree.chain)
|
|
{
|
|
if (run_pblock_chain(rtx, rtx->awk->tree.chain) == -1) return -1;
|
|
}
|
|
}
|
|
|
|
#undef ADJUST_ERROR
|
|
return 0;
|
|
}
|
|
|
|
static int run_pblock_chain (qse_awk_rtx_t* rtx, qse_awk_chain_t* chain)
|
|
{
|
|
qse_size_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 (qse_awk_rtx_t* rtx, qse_awk_chain_t* cha, qse_size_t bno)
|
|
{
|
|
qse_awk_nde_t* ptn;
|
|
qse_awk_nde_blk_t* blk;
|
|
|
|
ptn = cha->pattern;
|
|
blk = (qse_awk_nde_blk_t*)cha->action;
|
|
|
|
if (!ptn)
|
|
{
|
|
/* just execute the block */
|
|
rtx->active_block = blk;
|
|
if (run_block(rtx, blk) <= -1) return -1;
|
|
}
|
|
else
|
|
{
|
|
if (!ptn->next)
|
|
{
|
|
/* pattern { ... } */
|
|
qse_awk_val_t* v1;
|
|
|
|
v1 = eval_expression(rtx, ptn);
|
|
if (!v1) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, v1);
|
|
|
|
if (qse_awk_rtx_valtobool(rtx, v1))
|
|
{
|
|
rtx->active_block = blk;
|
|
if (run_block(rtx, blk) <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v1);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, v1);
|
|
}
|
|
else
|
|
{
|
|
/* pattern, pattern { ... } */
|
|
QSE_ASSERT (ptn->next->next == QSE_NULL);
|
|
QSE_ASSERT (rtx->pattern_range_state != QSE_NULL);
|
|
|
|
if (rtx->pattern_range_state[bno] == 0)
|
|
{
|
|
qse_awk_val_t* v1;
|
|
|
|
v1 = eval_expression(rtx, ptn);
|
|
if (!v1) return -1;
|
|
qse_awk_rtx_refupval (rtx, v1);
|
|
rtx->pattern_range_state[bno] = qse_awk_rtx_valtobool(rtx, v1);
|
|
qse_awk_rtx_refdownval (rtx, v1);
|
|
}
|
|
|
|
if (rtx->pattern_range_state[bno] == 1)
|
|
{
|
|
qse_awk_val_t* v2;
|
|
|
|
v2 = eval_expression(rtx, ptn->next);
|
|
if (!v2) return -1;
|
|
qse_awk_rtx_refupval (rtx, v2);
|
|
rtx->pattern_range_state[bno] = !qse_awk_rtx_valtobool(rtx, v2);
|
|
qse_awk_rtx_refdownval (rtx, v2);
|
|
|
|
rtx->active_block = blk;
|
|
if (run_block(rtx, blk) <= -1) return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int run_block0 (qse_awk_rtx_t* rtx, qse_awk_nde_blk_t* nde)
|
|
{
|
|
qse_awk_nde_t* p;
|
|
qse_size_t nlcls;
|
|
qse_size_t saved_stack_top;
|
|
int n = 0;
|
|
|
|
if (nde == QSE_NULL)
|
|
{
|
|
/* blockless pattern - execute print $0*/
|
|
qse_awk_rtx_refupval (rtx, rtx->inrec.d0);
|
|
|
|
n = qse_awk_rtx_writeiostr(rtx, QSE_AWK_OUT_CONSOLE, QSE_T(""), QSE_STR_PTR(&rtx->inrec.line), QSE_STR_LEN(&rtx->inrec.line));
|
|
if (n <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, rtx->inrec.d0);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
n = qse_awk_rtx_writeiostr(rtx, QSE_AWK_OUT_CONSOLE, QSE_T(""), rtx->gbl.ors.ptr, rtx->gbl.ors.len);
|
|
if (n <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, rtx->inrec.d0);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, rtx->inrec.d0);
|
|
return 0;
|
|
}
|
|
|
|
QSE_ASSERT (nde->type == QSE_AWK_NDE_BLK);
|
|
|
|
p = nde->body;
|
|
nlcls = nde->nlcls;
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (
|
|
QSE_T("securing space for local variables nlcls = %d\n"),
|
|
(int)nlcls);
|
|
#endif
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
|
|
/* secure space for local variables */
|
|
while (nlcls > 0)
|
|
{
|
|
--nlcls;
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1)
|
|
{
|
|
/* restore stack top */
|
|
rtx->stack_top = saved_stack_top;
|
|
return -1;
|
|
}
|
|
|
|
/* refupval is not required for qse_awk_val_nil */
|
|
}
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("executing block statements\n"));
|
|
#endif
|
|
|
|
while (p != QSE_NULL && rtx->exit_level == EXIT_NONE)
|
|
{
|
|
if (run_statement(rtx, p) <= -1)
|
|
{
|
|
n = -1;
|
|
break;
|
|
}
|
|
p = p->next;
|
|
}
|
|
|
|
/* pop off local variables */
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("popping off local variables\n"));
|
|
#endif
|
|
nlcls = nde->nlcls;
|
|
while (nlcls > 0)
|
|
{
|
|
--nlcls;
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_LCL(rtx,nlcls));
|
|
__raw_pop (rtx);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static int run_block (qse_awk_rtx_t* rtx, qse_awk_nde_blk_t* nde)
|
|
{
|
|
int n;
|
|
|
|
if (rtx->awk->opt.depth.s.block_run > 0 &&
|
|
rtx->depth.block >= rtx->awk->opt.depth.s.block_run)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EBLKNST, &nde->loc);
|
|
return -1;;
|
|
}
|
|
|
|
rtx->depth.block++;
|
|
n = run_block0(rtx, nde);
|
|
rtx->depth.block--;
|
|
|
|
return n;
|
|
}
|
|
|
|
#define ON_STATEMENT(rtx,nde) QSE_BLOCK ( \
|
|
qse_awk_rtx_ecb_t* ecb; \
|
|
if ((rtx)->awk->haltall) (rtx)->exit_level = EXIT_ABORT; \
|
|
for (ecb = (rtx)->ecb; ecb; ecb = ecb->next) \
|
|
if (ecb->stmt) ecb->stmt (rtx, nde); \
|
|
)
|
|
|
|
static int run_statement (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
int xret;
|
|
qse_awk_val_t* tmp;
|
|
|
|
ON_STATEMENT (rtx, nde);
|
|
|
|
switch (nde->type)
|
|
{
|
|
case QSE_AWK_NDE_NULL:
|
|
/* do nothing */
|
|
xret = 0;
|
|
break;
|
|
|
|
case QSE_AWK_NDE_BLK:
|
|
xret = run_block(rtx, (qse_awk_nde_blk_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_IF:
|
|
xret = run_if(rtx, (qse_awk_nde_if_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_WHILE:
|
|
case QSE_AWK_NDE_DOWHILE:
|
|
xret = run_while(rtx, (qse_awk_nde_while_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_FOR:
|
|
xret = run_for(rtx, (qse_awk_nde_for_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_FOREACH:
|
|
xret = run_foreach(rtx, (qse_awk_nde_foreach_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_BREAK:
|
|
xret = run_break(rtx, (qse_awk_nde_break_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_CONTINUE:
|
|
xret = run_continue(rtx, (qse_awk_nde_continue_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_RETURN:
|
|
xret = run_return(rtx, (qse_awk_nde_return_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_EXIT:
|
|
xret = run_exit(rtx, (qse_awk_nde_exit_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_NEXT:
|
|
xret = run_next(rtx, (qse_awk_nde_next_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_NEXTFILE:
|
|
xret = run_nextfile(rtx, (qse_awk_nde_nextfile_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_DELETE:
|
|
xret = run_delete(rtx, (qse_awk_nde_delete_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_RESET:
|
|
xret = run_reset(rtx, (qse_awk_nde_reset_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_PRINT:
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT) goto __fallback__;
|
|
xret = run_print(rtx, (qse_awk_nde_print_t*)nde);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_PRINTF:
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT) goto __fallback__;
|
|
xret = run_printf(rtx, (qse_awk_nde_print_t*)nde);
|
|
break;
|
|
|
|
default:
|
|
__fallback__:
|
|
tmp = eval_expression(rtx, nde);
|
|
if (tmp == QSE_NULL) xret = -1;
|
|
else
|
|
{
|
|
/* destroy the value if not referenced */
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
xret = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return xret;
|
|
}
|
|
|
|
static int run_if (qse_awk_rtx_t* rtx, qse_awk_nde_if_t* nde)
|
|
{
|
|
qse_awk_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 */
|
|
QSE_ASSERT (nde->test->next == QSE_NULL);
|
|
|
|
test = eval_expression (rtx, nde->test);
|
|
if (test == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, test);
|
|
if (qse_awk_rtx_valtobool(rtx, test))
|
|
{
|
|
n = run_statement(rtx, nde->then_part);
|
|
}
|
|
else if (nde->else_part)
|
|
{
|
|
n = run_statement(rtx, nde->else_part);
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, test); /* TODO: is this correct?*/
|
|
return n;
|
|
}
|
|
|
|
static int run_while (qse_awk_rtx_t* rtx, qse_awk_nde_while_t* nde)
|
|
{
|
|
qse_awk_val_t* test;
|
|
|
|
if (nde->type == QSE_AWK_NDE_WHILE)
|
|
{
|
|
/* no chained expressions are allowed for the test
|
|
* expression of the while statement */
|
|
QSE_ASSERT (nde->test->next == QSE_NULL);
|
|
|
|
while (1)
|
|
{
|
|
ON_STATEMENT (rtx, nde->test);
|
|
|
|
test = eval_expression(rtx, nde->test);
|
|
if (!test) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, test);
|
|
|
|
if (qse_awk_rtx_valtobool(rtx, test))
|
|
{
|
|
if (run_statement(rtx,nde->body) <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
break;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
break;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) break;
|
|
|
|
}
|
|
}
|
|
else if (nde->type == QSE_AWK_NDE_DOWHILE)
|
|
{
|
|
/* no chained expressions are allowed for the test
|
|
* expression of the while statement */
|
|
QSE_ASSERT (nde->test->next == QSE_NULL);
|
|
|
|
do
|
|
{
|
|
if (run_statement(rtx,nde->body) <= -1) return -1;
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
break;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) break;
|
|
|
|
ON_STATEMENT (rtx, nde->test);
|
|
|
|
test = eval_expression(rtx, nde->test);
|
|
if (!test) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, test);
|
|
|
|
if (!qse_awk_rtx_valtobool(rtx, test))
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
break;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
}
|
|
while (1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_for (qse_awk_rtx_t* rtx, qse_awk_nde_for_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
|
|
if (nde->init != QSE_NULL)
|
|
{
|
|
QSE_ASSERT (nde->init->next == QSE_NULL);
|
|
|
|
ON_STATEMENT (rtx, nde->init);
|
|
val = eval_expression(rtx,nde->init);
|
|
if (val == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (nde->test != QSE_NULL)
|
|
{
|
|
qse_awk_val_t* test;
|
|
|
|
/* no chained expressions for the test expression of
|
|
* the for statement are allowed */
|
|
QSE_ASSERT (nde->test->next == QSE_NULL);
|
|
|
|
ON_STATEMENT (rtx, nde->test);
|
|
test = eval_expression (rtx, nde->test);
|
|
if (test == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, test);
|
|
if (qse_awk_rtx_valtobool(rtx, test))
|
|
{
|
|
if (run_statement(rtx,nde->body) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
break;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, test);
|
|
}
|
|
else
|
|
{
|
|
if (run_statement(rtx,nde->body) == -1) return -1;
|
|
}
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
break;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) break;
|
|
|
|
if (nde->incr != QSE_NULL)
|
|
{
|
|
QSE_ASSERT (nde->incr->next == QSE_NULL);
|
|
|
|
ON_STATEMENT (rtx, nde->incr);
|
|
val = eval_expression (rtx, nde->incr);
|
|
if (val == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct foreach_walker_t
|
|
{
|
|
qse_awk_rtx_t* rtx;
|
|
qse_awk_nde_t* var;
|
|
qse_awk_nde_t* body;
|
|
int ret;
|
|
};
|
|
|
|
static qse_htb_walk_t walk_foreach (qse_htb_t* map, qse_htb_pair_t* pair, void* arg)
|
|
{
|
|
struct foreach_walker_t* w = (struct foreach_walker_t*)arg;
|
|
qse_awk_val_t* str;
|
|
|
|
str = (qse_awk_val_t*)qse_awk_rtx_makestrval(w->rtx, QSE_HTB_KPTR(pair), QSE_HTB_KLEN(pair));
|
|
if (str == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (w->rtx, &w->var->loc);
|
|
w->ret = -1;
|
|
return QSE_HTB_WALK_STOP;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (w->rtx, str);
|
|
if (do_assignment(w->rtx, w->var, str) == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (w->rtx, str);
|
|
w->ret = -1;
|
|
return QSE_HTB_WALK_STOP;
|
|
}
|
|
|
|
if (run_statement(w->rtx, w->body) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (w->rtx, str);
|
|
w->ret = -1;
|
|
return QSE_HTB_WALK_STOP;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (w->rtx, str);
|
|
|
|
if (w->rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
w->rtx->exit_level = EXIT_NONE;
|
|
return QSE_HTB_WALK_STOP;
|
|
}
|
|
else if (w->rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
w->rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (w->rtx->exit_level != EXIT_NONE)
|
|
{
|
|
return QSE_HTB_WALK_STOP;
|
|
}
|
|
|
|
return QSE_HTB_WALK_FORWARD;
|
|
}
|
|
|
|
static int run_foreach (qse_awk_rtx_t* rtx, qse_awk_nde_foreach_t* nde)
|
|
{
|
|
qse_awk_nde_exp_t* test;
|
|
qse_awk_val_t* rv;
|
|
qse_htb_t* map;
|
|
struct foreach_walker_t walker;
|
|
qse_awk_val_type_t rvtype;
|
|
|
|
test = (qse_awk_nde_exp_t*)nde->test;
|
|
QSE_ASSERT (test->type == QSE_AWK_NDE_EXP_BIN && test->opcode == QSE_AWK_BINOP_IN);
|
|
|
|
/* chained expressions should not be allowed
|
|
* by the parser first of all */
|
|
QSE_ASSERT (test->right->next == QSE_NULL);
|
|
|
|
rv = eval_expression(rtx, test->right);
|
|
if (rv == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, rv);
|
|
rvtype = QSE_AWK_RTX_GETVALTYPE(rtx, rv);
|
|
if (rvtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
/* just return without excuting the loop body */
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
return 0;
|
|
}
|
|
else if (rvtype != QSE_AWK_VAL_MAP)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOTMAPIN, &test->right->loc);
|
|
return -1;
|
|
}
|
|
map = ((qse_awk_val_map_t*)rv)->map;
|
|
|
|
walker.rtx = rtx;
|
|
walker.var = test->left;
|
|
walker.body = nde->body;
|
|
walker.ret = 0;
|
|
qse_htb_walk (map, walk_foreach, &walker);
|
|
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
return walker.ret;
|
|
}
|
|
|
|
static int run_break (qse_awk_rtx_t* run, qse_awk_nde_break_t* nde)
|
|
{
|
|
run->exit_level = EXIT_BREAK;
|
|
return 0;
|
|
}
|
|
|
|
static int run_continue (qse_awk_rtx_t* rtx, qse_awk_nde_continue_t* nde)
|
|
{
|
|
rtx->exit_level = EXIT_CONTINUE;
|
|
return 0;
|
|
}
|
|
|
|
static int run_return (qse_awk_rtx_t* rtx, qse_awk_nde_return_t* nde)
|
|
{
|
|
if (nde->val != QSE_NULL)
|
|
{
|
|
qse_awk_val_t* val;
|
|
|
|
/* chained expressions should not be allowed
|
|
* by the parser first of all */
|
|
QSE_ASSERT (nde->val->next == QSE_NULL);
|
|
|
|
val = eval_expression (rtx, nde->val);
|
|
if (val == QSE_NULL) return -1;
|
|
|
|
if (!(rtx->awk->opt.trait & QSE_AWK_FLEXMAP))
|
|
{
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, val) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* cannot return a map */
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
SETERR_LOC (rtx, QSE_AWK_EMAPRET, &nde->loc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_RETVAL(rtx));
|
|
RTX_STACK_RETVAL(rtx) = val;
|
|
|
|
/* NOTE: see eval_call() for the trick */
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
}
|
|
|
|
rtx->exit_level = EXIT_FUNCTION;
|
|
return 0;
|
|
}
|
|
|
|
static int run_exit (qse_awk_rtx_t* rtx, qse_awk_nde_exit_t* nde)
|
|
{
|
|
if (nde->val != QSE_NULL)
|
|
{
|
|
qse_awk_val_t* val;
|
|
|
|
/* chained expressions should not be allowed
|
|
* by the parser first of all */
|
|
QSE_ASSERT (nde->val->next == QSE_NULL);
|
|
|
|
val = eval_expression (rtx, nde->val);
|
|
if (val == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_RETVAL_GBL(rtx));
|
|
RTX_STACK_RETVAL_GBL(rtx) = val; /* global return value */
|
|
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
}
|
|
|
|
rtx->exit_level = (nde->abort)? EXIT_ABORT: EXIT_GLOBAL;
|
|
return 0;
|
|
}
|
|
|
|
static int run_next (qse_awk_rtx_t* run, qse_awk_nde_next_t* nde)
|
|
{
|
|
/* the parser checks if next has been called in the begin/end
|
|
* block or whereever inappropriate. so the runtime doesn't
|
|
* check that explicitly */
|
|
if (run->active_block == (qse_awk_nde_blk_t*)run->awk->tree.begin)
|
|
{
|
|
SETERR_LOC (run, QSE_AWK_ERNEXTBEG, &nde->loc);
|
|
return -1;
|
|
}
|
|
else if (run->active_block == (qse_awk_nde_blk_t*)run->awk->tree.end)
|
|
{
|
|
SETERR_LOC (run, QSE_AWK_ERNEXTEND, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
run->exit_level = EXIT_NEXT;
|
|
return 0;
|
|
}
|
|
|
|
static int run_nextinfile (qse_awk_rtx_t* rtx, qse_awk_nde_nextfile_t* nde)
|
|
{
|
|
int n;
|
|
|
|
/* normal nextfile statement */
|
|
if (rtx->active_block == (qse_awk_nde_blk_t*)rtx->awk->tree.begin)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ERNEXTFBEG, &nde->loc);
|
|
return -1;
|
|
}
|
|
else if (rtx->active_block == (qse_awk_nde_blk_t*)rtx->awk->tree.end)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ERNEXTFEND, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
n = qse_awk_rtx_nextio_read (rtx, QSE_AWK_IN_CONSOLE, QSE_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 (qse_awk_rtx_t* rtx, qse_awk_nde_nextfile_t* nde)
|
|
{
|
|
int n;
|
|
|
|
/* nextofile can be called from BEGIN and END block unlike nextfile */
|
|
|
|
n = qse_awk_rtx_nextio_write (rtx, QSE_AWK_OUT_CONSOLE, QSE_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 (qse_awk_rtx_t* rtx, qse_awk_nde_nextfile_t* nde)
|
|
{
|
|
return (nde->out)? run_nextoutfile(rtx, nde): run_nextinfile(rtx, nde);
|
|
}
|
|
|
|
static int delete_indexed (qse_awk_rtx_t* rtx, qse_htb_t* map, qse_awk_nde_var_t* var)
|
|
{
|
|
qse_char_t* iptr;
|
|
qse_size_t ilen;
|
|
qse_char_t idxbuf[IDXBUFSIZE];
|
|
|
|
QSE_ASSERT (var->idx != QSE_NULL);
|
|
|
|
/* delete x["abc"];
|
|
* delete x[20,"abc"]; */
|
|
ilen = QSE_COUNTOF(idxbuf);
|
|
iptr = idxnde_to_str(rtx, var->idx, idxbuf, &ilen);
|
|
if (!iptr) return -1;
|
|
|
|
qse_htb_delete (map, iptr, ilen);
|
|
|
|
if (iptr != idxbuf) qse_awk_rtx_freemem (rtx, iptr);
|
|
return 0;
|
|
}
|
|
|
|
static int run_delete_named (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
QSE_ASSERTX (
|
|
(var->type == QSE_AWK_NDE_NAMED && var->idx == QSE_NULL) ||
|
|
(var->type == QSE_AWK_NDE_NAMEDIDX && var->idx != QSE_NULL),
|
|
"if a named variable has an index part and a named indexed variable doesn't have an index part, the program is definitely wrong");
|
|
|
|
pair = qse_htb_search (
|
|
rtx->named, var->id.name.ptr, var->id.name.len);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
|
|
/* value not set for the named variable.
|
|
* create a map and assign it to the variable */
|
|
|
|
tmp = qse_awk_rtx_makemapval (rtx);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
/* adjust error line */
|
|
ADJERR_LOC (rtx, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
pair = qse_htb_upsert (rtx->named,
|
|
var->id.name.ptr, var->id.name.len, tmp, 0);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
/* as this is the assignment, it needs to update
|
|
* the reference count of the target value. */
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
}
|
|
else
|
|
{
|
|
qse_awk_val_t* val;
|
|
qse_htb_t* map;
|
|
|
|
val = (qse_awk_val_t*)QSE_HTB_VPTR(pair);
|
|
QSE_ASSERT (val != QSE_NULL);
|
|
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, val) != QSE_AWK_VAL_MAP)
|
|
{
|
|
SETERR_ARGX_LOC (
|
|
rtx, QSE_AWK_ENOTDEL,
|
|
&var->id.name, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
map = ((qse_awk_val_map_t*)val)->map;
|
|
if (var->type == QSE_AWK_NDE_NAMEDIDX)
|
|
{
|
|
if (delete_indexed (rtx, map, var) <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_htb_clear (map);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_delete_unnamed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var)
|
|
{
|
|
qse_awk_val_t* val;
|
|
qse_awk_val_type_t vtype;
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_GBL:
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
val = RTX_STACK_GBL (rtx,var->id.idxa);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
val = RTX_STACK_LCL (rtx,var->id.idxa);
|
|
break;
|
|
|
|
default:
|
|
val = RTX_STACK_ARG (rtx,var->id.idxa);
|
|
break;
|
|
}
|
|
|
|
QSE_ASSERT (val != QSE_NULL);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, val);
|
|
if (vtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
|
|
/* value not set.
|
|
* create a map and assign it to the variable */
|
|
|
|
tmp = qse_awk_rtx_makemapval (rtx);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
/* no need to reduce the reference count of
|
|
* the previous value because it was nil. */
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_GBL:
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
{
|
|
int x;
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
x = qse_awk_rtx_setgbl(rtx, (int)var->id.idxa, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
|
|
if (x <= -1)
|
|
{
|
|
ADJERR_LOC (rtx, &var->loc);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
RTX_STACK_LCL(rtx,var->id.idxa) = tmp;
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
break;
|
|
|
|
default:
|
|
RTX_STACK_ARG(rtx,var->id.idxa) = tmp;
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_htb_t* map;
|
|
|
|
if (vtype != QSE_AWK_VAL_MAP)
|
|
{
|
|
SETERR_ARGX_LOC (
|
|
rtx, QSE_AWK_ENOTDEL,
|
|
&var->id.name, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
map = ((qse_awk_val_map_t*)val)->map;
|
|
if (var->type == QSE_AWK_NDE_GBLIDX ||
|
|
var->type == QSE_AWK_NDE_LCLIDX ||
|
|
var->type == QSE_AWK_NDE_ARGIDX)
|
|
{
|
|
if (delete_indexed (rtx, map, var) <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_htb_clear (map);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_delete (qse_awk_rtx_t* rtx, qse_awk_nde_delete_t* nde)
|
|
{
|
|
qse_awk_nde_var_t* var;
|
|
|
|
var = (qse_awk_nde_var_t*) nde->var;
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMED:
|
|
case QSE_AWK_NDE_NAMEDIDX:
|
|
return run_delete_named (rtx, var);
|
|
|
|
case QSE_AWK_NDE_GBL:
|
|
case QSE_AWK_NDE_LCL:
|
|
case QSE_AWK_NDE_ARG:
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
case QSE_AWK_NDE_ARGIDX:
|
|
return run_delete_unnamed (rtx, var);
|
|
|
|
default:
|
|
QSE_ASSERTX (
|
|
!"should never happen - wrong target for delete",
|
|
"the delete statement cannot be called with other nodes than the variables such as a named variable, a named indexed variable, etc");
|
|
|
|
SETERR_LOC (rtx, QSE_AWK_EBADARG, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
static int reset_variable (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var)
|
|
{
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMED:
|
|
QSE_ASSERTX (
|
|
var->type == QSE_AWK_NDE_NAMED && var->idx == QSE_NULL,
|
|
"if a named variable has an index part, something is definitely wrong");
|
|
|
|
/* a named variable can be reset if removed from a internal map
|
|
to manage it */
|
|
qse_htb_delete (rtx->named, var->id.name.ptr, var->id.name.len);
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_GBL:
|
|
case QSE_AWK_NDE_LCL:
|
|
case QSE_AWK_NDE_ARG:
|
|
{
|
|
qse_awk_val_t* val;
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_GBL:
|
|
val = RTX_STACK_GBL(rtx,var->id.idxa);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
val = RTX_STACK_LCL(rtx,var->id.idxa);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_ARG:
|
|
val = RTX_STACK_ARG(rtx,var->id.idxa);
|
|
break;
|
|
}
|
|
|
|
QSE_ASSERT (val != QSE_NULL);
|
|
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, val) != QSE_AWK_VAL_NIL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_GBL:
|
|
RTX_STACK_GBL(rtx,var->id.idxa) = qse_awk_val_nil;
|
|
break;
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
RTX_STACK_LCL(rtx,var->id.idxa) = qse_awk_val_nil;
|
|
break;
|
|
|
|
case QSE_AWK_NDE_ARG:
|
|
RTX_STACK_ARG(rtx,var->id.idxa) = qse_awk_val_nil;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
QSE_ASSERTX (
|
|
!"should never happen - wrong target for reset",
|
|
"the reset statement can only be called with plain variables");
|
|
SETERR_LOC (rtx, QSE_AWK_EBADARG, &var->loc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int run_reset (qse_awk_rtx_t* rtx, qse_awk_nde_reset_t* nde)
|
|
{
|
|
return reset_variable (rtx, (qse_awk_nde_var_t*)nde->var);
|
|
}
|
|
|
|
static int run_print (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
|
|
{
|
|
qse_char_t* out = QSE_NULL;
|
|
qse_awk_val_t* out_v = QSE_NULL;
|
|
const qse_char_t* dst;
|
|
int n, xret = 0;
|
|
|
|
QSE_ASSERT (
|
|
(nde->out_type == QSE_AWK_OUT_PIPE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_RWPIPE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_FILE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_APFILE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_CONSOLE && nde->out == QSE_NULL));
|
|
|
|
/* check if destination has been specified. */
|
|
if (nde->out)
|
|
{
|
|
qse_size_t len;
|
|
|
|
/* if so, resolve the destination name */
|
|
out_v = eval_expression(rtx, nde->out);
|
|
if (!out_v) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, out_v);
|
|
|
|
out = qse_awk_rtx_getvalstr(rtx, out_v, &len);
|
|
if (!out) goto oops;
|
|
|
|
if (len <= 0)
|
|
{
|
|
/* the destination name is empty */
|
|
SETERR_LOC (rtx, QSE_AWK_EIONMEM, &nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
/* it needs to check if the destination name contains
|
|
* any invalid characters to the underlying system */
|
|
while (len > 0)
|
|
{
|
|
if (out[--len] == QSE_T('\0'))
|
|
{
|
|
/* provide length up to one character before
|
|
* the first null not to contains a null
|
|
* in an error message */
|
|
SETERR_ARG_LOC (
|
|
rtx, QSE_AWK_EIONMNL,
|
|
out, qse_strlen(out), &nde->loc);
|
|
|
|
/* if so, it skips writing */
|
|
goto oops_1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* transforms the destination to suit the usage with io */
|
|
dst = (out == QSE_NULL)? QSE_T(""): out;
|
|
|
|
/* check if print is followed by any arguments */
|
|
if (!nde->args)
|
|
{
|
|
/* if it doesn't have any arguments, print the entire
|
|
* input record */
|
|
n = qse_awk_rtx_writeiostr(rtx, nde->out_type, dst, QSE_STR_PTR(&rtx->inrec.line), QSE_STR_LEN(&rtx->inrec.line));
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if it has any arguments, print the arguments separated by
|
|
* the value OFS */
|
|
qse_awk_nde_t* head, * np;
|
|
qse_awk_val_t* v;
|
|
|
|
if (nde->args->type == QSE_AWK_NDE_GRP)
|
|
{
|
|
/* parenthesized print */
|
|
QSE_ASSERT (nde->args->next == QSE_NULL);
|
|
head = ((qse_awk_nde_grp_t*)nde->args)->body;
|
|
}
|
|
else head = nde->args;
|
|
|
|
for (np = head; np != QSE_NULL; np = np->next)
|
|
{
|
|
if (np != head)
|
|
{
|
|
n = qse_awk_rtx_writeiostr(rtx, nde->out_type, dst, rtx->gbl.ofs.ptr, rtx->gbl.ofs.len);
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
|
|
v = eval_expression(rtx, np);
|
|
if (v == QSE_NULL) goto oops_1;
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_writeioval(rtx, nde->out_type, dst, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* print the value ORS to terminate the operation */
|
|
n = qse_awk_rtx_writeiostr(rtx, nde->out_type, dst, rtx->gbl.ors.ptr, rtx->gbl.ors.len);
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_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)
|
|
{
|
|
if (out) qse_awk_rtx_freevalstr (rtx, out_v, out);
|
|
qse_awk_rtx_refdownval (rtx, out_v);
|
|
}
|
|
|
|
return xret;
|
|
|
|
oops:
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
|
|
oops_1:
|
|
if (out_v)
|
|
{
|
|
if (out) qse_awk_rtx_freevalstr (rtx, out_v, out);
|
|
qse_awk_rtx_refdownval (rtx, out_v);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int run_printf (qse_awk_rtx_t* rtx, qse_awk_nde_print_t* nde)
|
|
{
|
|
qse_char_t* out = QSE_NULL;
|
|
qse_awk_val_t* out_v = QSE_NULL;
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
const qse_char_t* dst;
|
|
qse_awk_nde_t* head;
|
|
int n, xret = 0;
|
|
|
|
QSE_ASSERT (
|
|
(nde->out_type == QSE_AWK_OUT_PIPE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_RWPIPE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_FILE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_APFILE && nde->out != QSE_NULL) ||
|
|
(nde->out_type == QSE_AWK_OUT_CONSOLE && nde->out == QSE_NULL));
|
|
|
|
if (nde->out)
|
|
{
|
|
qse_size_t len;
|
|
|
|
out_v = eval_expression(rtx, nde->out);
|
|
if (!out_v) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, out_v);
|
|
|
|
out = qse_awk_rtx_getvalstr(rtx, out_v, &len);
|
|
if (!out) goto oops;
|
|
|
|
if (len <= 0)
|
|
{
|
|
/* the output destination name is empty. */
|
|
SETERR_LOC (rtx, QSE_AWK_EIONMEM, &nde->loc);
|
|
goto oops_1;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
if (out[--len] == QSE_T('\0'))
|
|
{
|
|
/* provide length up to one character before
|
|
* the first null not to contains a null
|
|
* in an error message */
|
|
SETERR_ARG_LOC (rtx, QSE_AWK_EIONMNL, out, qse_strlen(out), &nde->loc);
|
|
|
|
/* the output destination name contains a null
|
|
* character. */
|
|
goto oops_1;
|
|
}
|
|
}
|
|
}
|
|
|
|
dst = (out == QSE_NULL)? QSE_T(""): out;
|
|
|
|
QSE_ASSERTX (nde->args != QSE_NULL,
|
|
"valid printf statement should have at least one argument. the parser must ensure this.");
|
|
|
|
if (nde->args->type == QSE_AWK_NDE_GRP)
|
|
{
|
|
/* parenthesized print */
|
|
QSE_ASSERT (nde->args->next == QSE_NULL);
|
|
head = ((qse_awk_nde_grp_t*)nde->args)->body;
|
|
}
|
|
else head = nde->args;
|
|
|
|
QSE_ASSERTX (head != QSE_NULL,
|
|
"valid printf statement should have at least one argument. the parser must ensure this.");
|
|
|
|
v = eval_expression(rtx, head);
|
|
if (v == QSE_NULL) goto oops_1;
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
vtype = QSE_AWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case QSE_AWK_VAL_STR:
|
|
/* perform the formatted output */
|
|
n = output_formatted (rtx, nde->out_type, dst, ((qse_awk_val_str_t*)v)->val.ptr, ((qse_awk_val_str_t*)v)->val.len, head->next);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
if (n == PRINT_IOERR) xret = n;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
/* perform the formatted output */
|
|
n = output_formatted_bytes(rtx, nde->out_type, dst, ((qse_awk_val_mbs_t*)v)->val.ptr, ((qse_awk_val_mbs_t*)v)->val.len, head->next);
|
|
qse_awk_rtx_refdownval (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 = qse_awk_rtx_writeioval(rtx, nde->out_type, dst, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT) xret = PRINT_IOERR;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (qse_awk_rtx_flushio(rtx, nde->out_type, dst) <= -1)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_TOLERANT) xret = PRINT_IOERR;
|
|
else goto oops_1;
|
|
}
|
|
|
|
if (out_v)
|
|
{
|
|
if (out) qse_awk_rtx_freevalstr (rtx, out_v, out);
|
|
qse_awk_rtx_refdownval (rtx, out_v);
|
|
}
|
|
|
|
return xret;
|
|
|
|
oops:
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
|
|
oops_1:
|
|
if (out_v)
|
|
{
|
|
if (out) qse_awk_rtx_freevalstr (rtx, out_v, out);
|
|
qse_awk_rtx_refdownval (rtx, out_v);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int output_formatted (
|
|
qse_awk_rtx_t* rtx, int out_type, const qse_char_t* dst,
|
|
const qse_char_t* fmt, qse_size_t fmt_len, qse_awk_nde_t* args)
|
|
{
|
|
qse_char_t* ptr;
|
|
qse_size_t len;
|
|
int n;
|
|
|
|
ptr = qse_awk_rtx_format(rtx, QSE_NULL, QSE_NULL, fmt, fmt_len, 0, args, &len);
|
|
if (!ptr) return -1;
|
|
|
|
n = qse_awk_rtx_writeiostr(rtx, out_type, dst, ptr, len);
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
return (rtx->awk->opt.trait & QSE_AWK_TOLERANT)? PRINT_IOERR: -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int output_formatted_bytes (
|
|
qse_awk_rtx_t* rtx, int out_type, const qse_char_t* dst,
|
|
const qse_mchar_t* fmt, qse_size_t fmt_len, qse_awk_nde_t* args)
|
|
{
|
|
qse_mchar_t* ptr;
|
|
qse_size_t len;
|
|
int n;
|
|
|
|
ptr = qse_awk_rtx_formatmbs(rtx, QSE_NULL, QSE_NULL, fmt, fmt_len, 0, args, &len);
|
|
if (!ptr) return -1;
|
|
|
|
n = qse_awk_rtx_writeiobytes(rtx, out_type, dst, ptr, len);
|
|
if (n <= -1 /*&& rtx->errinf.num != QSE_AWK_EIOIMPL*/)
|
|
{
|
|
return (rtx->awk->opt.trait & QSE_AWK_TOLERANT)? PRINT_IOERR: -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_expression (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* v;
|
|
int n;
|
|
|
|
#if 0
|
|
if (rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* returns QSE_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 = QSE_AWK_ENOERR;
|
|
return QSE_NULL;
|
|
}
|
|
#endif
|
|
|
|
v = eval_expression0 (rtx, nde);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, v) == QSE_AWK_VAL_REX)
|
|
{
|
|
qse_cstr_t vs;
|
|
|
|
/* special case where a regular expression is used in
|
|
* without any match operators:
|
|
* print /abc/;
|
|
* perform match against $0.
|
|
*/
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
|
|
if (QSE_AWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == QSE_AWK_VAL_NIL)
|
|
{
|
|
/* the record has never been read.
|
|
* probably, this function has been triggered
|
|
* by the statements in the BEGIN block */
|
|
vs.ptr = QSE_T("");
|
|
vs.len = 0;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERTX (
|
|
QSE_AWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == QSE_AWK_VAL_STR,
|
|
"the internal value representing $0 should always be of the string type once it has been set/updated. it is nil initially.");
|
|
|
|
vs.ptr = ((qse_awk_val_str_t*)rtx->inrec.d0)->val.ptr;
|
|
vs.len = ((qse_awk_val_str_t*)rtx->inrec.d0)->val.len;
|
|
}
|
|
|
|
n = qse_awk_rtx_matchrex(rtx, v, &vs, &vs, QSE_NULL, QSE_NULL);
|
|
if (n <= -1)
|
|
{
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
return QSE_NULL;
|
|
}
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
v = qse_awk_rtx_makeintval(rtx, (n != 0));
|
|
if (v == QSE_NULL)
|
|
{
|
|
/* adjust error line */
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_expression0 (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
static eval_expr_t __evaluator[] =
|
|
{
|
|
/* the order of functions here should match the order
|
|
* of node types(qse_awk_nde_type_t) declared in qse/awk/awk.h */
|
|
eval_group,
|
|
eval_assignment,
|
|
eval_binary,
|
|
eval_unary,
|
|
eval_incpre,
|
|
eval_incpst,
|
|
eval_cnd,
|
|
eval_fncall_fnc,
|
|
eval_fncall_fun,
|
|
eval_fncall_var,
|
|
eval_int,
|
|
eval_flt,
|
|
eval_str,
|
|
eval_mbs,
|
|
eval_rex,
|
|
eval_fun,
|
|
eval_named,
|
|
eval_gbl,
|
|
eval_lcl,
|
|
eval_arg,
|
|
eval_namedidx,
|
|
eval_gblidx,
|
|
eval_lclidx,
|
|
eval_argidx,
|
|
eval_pos,
|
|
eval_getline,
|
|
eval_print,
|
|
eval_printf
|
|
};
|
|
|
|
qse_awk_val_t* v;
|
|
|
|
QSE_ASSERT (nde->type >= QSE_AWK_NDE_GRP && (nde->type - QSE_AWK_NDE_GRP) < QSE_COUNTOF(__evaluator));
|
|
|
|
v = __evaluator[nde->type-QSE_AWK_NDE_GRP](rtx, nde);
|
|
|
|
if (v != QSE_NULL && rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
/* returns QSE_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 = QSE_AWK_ENOERR;
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_group (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
#if 0
|
|
/* eval_binop_in evaluates the QSE_AWK_NDE_GRP specially.
|
|
* so this function should never be reached. */
|
|
QSE_ASSERT (!"should never happen - NDE_GRP only for in");
|
|
SETERR_LOC (rtx, QSE_AWK_EINTERN, &nde->loc);
|
|
return QSE_NULL;
|
|
#endif
|
|
|
|
/* a group can be evauluated in a normal context
|
|
* if a program is parsed with QSE_AWK_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. */
|
|
|
|
qse_awk_val_t* val;
|
|
qse_awk_nde_t* np;
|
|
|
|
np = ((qse_awk_nde_grp_t*)nde)->body;
|
|
|
|
QSE_ASSERT (np != QSE_NULL);
|
|
|
|
loop:
|
|
val = eval_expression (rtx, np);
|
|
if (val == QSE_NULL) return QSE_NULL;
|
|
|
|
np = np->next;
|
|
if (np)
|
|
{
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
goto loop;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_assignment (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val, * ret;
|
|
qse_awk_nde_ass_t* ass = (qse_awk_nde_ass_t*)nde;
|
|
|
|
QSE_ASSERT (ass->left != QSE_NULL);
|
|
QSE_ASSERT (ass->right != QSE_NULL);
|
|
|
|
QSE_ASSERT (ass->right->next == QSE_NULL);
|
|
val = eval_expression (rtx, ass->right);
|
|
if (val == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
|
|
if (ass->opcode != QSE_AWK_ASSOP_NONE)
|
|
{
|
|
qse_awk_val_t* val2, * tmp;
|
|
static binop_func_t binop_func[] =
|
|
{
|
|
/* this table must match qse_awk_assop_type_t in rtx.h */
|
|
QSE_NULL, /* QSE_AWK_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
|
|
};
|
|
|
|
QSE_ASSERT (ass->left->next == QSE_NULL);
|
|
val2 = eval_expression(rtx, ass->left);
|
|
if (val2 == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, val2);
|
|
|
|
QSE_ASSERT (ass->opcode >= 0);
|
|
QSE_ASSERT (ass->opcode < QSE_COUNTOF(binop_func));
|
|
QSE_ASSERT (binop_func[ass->opcode] != QSE_NULL);
|
|
|
|
tmp = binop_func[ass->opcode](rtx, val2, val);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, val2);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, val2);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
|
|
val = tmp;
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
}
|
|
|
|
ret = do_assignment(rtx, ass->left, val);
|
|
qse_awk_rtx_refdownval (rtx, val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static qse_awk_val_t* do_assignment (qse_awk_rtx_t* rtx, qse_awk_nde_t* var, qse_awk_val_t* val)
|
|
{
|
|
qse_awk_val_t* ret;
|
|
qse_awk_errnum_t errnum;
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMED:
|
|
case QSE_AWK_NDE_GBL:
|
|
case QSE_AWK_NDE_LCL:
|
|
case QSE_AWK_NDE_ARG:
|
|
ret = do_assignment_nonidx(rtx, (qse_awk_nde_var_t*)var, val);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_NAMEDIDX:
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
case QSE_AWK_NDE_ARGIDX:
|
|
if (QSE_AWK_RTX_GETVALTYPE(rtx, val) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* a map cannot become a member of a map */
|
|
errnum = QSE_AWK_EMAPTOIDX;
|
|
goto exit_on_error;
|
|
}
|
|
|
|
ret = do_assignment_idx(rtx, (qse_awk_nde_var_t*)var, val);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_POS:
|
|
if (QSE_AWK_RTX_GETVALTYPE(rtx, val) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* a map cannot be assigned to a positional */
|
|
errnum = QSE_AWK_EMAPTOPOS;
|
|
goto exit_on_error;
|
|
}
|
|
|
|
ret = do_assignment_pos(rtx, (qse_awk_nde_pos_t*)var, val);
|
|
break;
|
|
|
|
default:
|
|
QSE_ASSERT (!"should never happen - invalid variable type");
|
|
errnum = QSE_AWK_EINTERN;
|
|
goto exit_on_error;
|
|
}
|
|
|
|
return ret;
|
|
|
|
exit_on_error:
|
|
SETERR_LOC (rtx, errnum, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
static qse_awk_val_t* do_assignment_nonidx (qse_awk_rtx_t* run, qse_awk_nde_var_t* var, qse_awk_val_t* val)
|
|
{
|
|
qse_awk_val_type_t vtype;
|
|
|
|
QSE_ASSERT (
|
|
var->type == QSE_AWK_NDE_NAMED ||
|
|
var->type == QSE_AWK_NDE_GBL ||
|
|
var->type == QSE_AWK_NDE_LCL ||
|
|
var->type == QSE_AWK_NDE_ARG
|
|
);
|
|
|
|
QSE_ASSERT (var->idx == QSE_NULL);
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, val);
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMED:
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
pair = qse_htb_search (run->named, var->id.name.ptr, var->id.name.len);
|
|
|
|
if (!(run->awk->opt.trait & QSE_AWK_FLEXMAP))
|
|
{
|
|
if (pair && QSE_AWK_RTX_GETVALTYPE (rtx, (qse_awk_val_t*)QSE_HTB_VPTR(pair)) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
qse_awk_errnum_t errnum;
|
|
errnum = (vtype == QSE_AWK_VAL_MAP)? QSE_AWK_ENMAPTOMAP: QSE_AWK_ENMAPTOSCALAR;
|
|
SETERR_ARGX_LOC (run, errnum, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
else if (vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* 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. */
|
|
SETERR_ARGX_LOC (run, QSE_AWK_EMAPTONVAR, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
if (qse_htb_upsert (run->named, var->id.name.ptr, var->id.name.len, val, 0) == QSE_NULL)
|
|
{
|
|
SETERR_LOC (run, QSE_AWK_ENOMEM, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (run, val);
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_GBL:
|
|
{
|
|
if (set_global(run, var->id.idxa, var, val, 1) == -1)
|
|
{
|
|
ADJERR_LOC (run, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
{
|
|
qse_awk_val_t* old = RTX_STACK_LCL(run,var->id.idxa);
|
|
|
|
if (!(run->awk->opt.trait & QSE_AWK_FLEXMAP))
|
|
{
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, old) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
qse_awk_errnum_t errnum;
|
|
errnum = (vtype == QSE_AWK_VAL_MAP)? QSE_AWK_ENMAPTOMAP: QSE_AWK_ENMAPTOSCALAR;
|
|
SETERR_ARGX_LOC (run, errnum, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
else if (vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* 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. */
|
|
SETERR_ARGX_LOC (run, QSE_AWK_EMAPTONVAR, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (run, old);
|
|
RTX_STACK_LCL(run,var->id.idxa) = val;
|
|
qse_awk_rtx_refupval (run, val);
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_ARG:
|
|
{
|
|
qse_awk_val_t* old = RTX_STACK_ARG(run,var->id.idxa);
|
|
|
|
if (!(run->awk->opt.trait & QSE_AWK_FLEXMAP))
|
|
{
|
|
if (QSE_AWK_RTX_GETVALTYPE(rtx, old) == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
qse_awk_errnum_t errnum;
|
|
errnum = (vtype == QSE_AWK_VAL_MAP)? QSE_AWK_ENMAPTOMAP: QSE_AWK_ENMAPTOSCALAR;
|
|
SETERR_ARGX_LOC (run, errnum, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
else if (vtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
/* 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. */
|
|
SETERR_ARGX_LOC (run, QSE_AWK_EMAPTONVAR, &var->id.name, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (run, old);
|
|
RTX_STACK_ARG(run,var->id.idxa) = val;
|
|
qse_awk_rtx_refupval (run, val);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* do_assignment_idx (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var, qse_awk_val_t* val)
|
|
{
|
|
qse_awk_val_map_t* map;
|
|
qse_awk_val_type_t mvtype;
|
|
qse_char_t* str;
|
|
qse_size_t len;
|
|
qse_char_t idxbuf[IDXBUFSIZE];
|
|
|
|
QSE_ASSERT (
|
|
(var->type == QSE_AWK_NDE_NAMEDIDX ||
|
|
var->type == QSE_AWK_NDE_GBLIDX ||
|
|
var->type == QSE_AWK_NDE_LCLIDX ||
|
|
var->type == QSE_AWK_NDE_ARGIDX) && var->idx != QSE_NULL);
|
|
QSE_ASSERT (QSE_AWK_RTX_GETVALTYPE (rtx, val) != QSE_AWK_VAL_MAP);
|
|
|
|
retry:
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMEDIDX:
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
pair = qse_htb_search(rtx->named, var->id.name.ptr, var->id.name.len);
|
|
map = (pair == QSE_NULL)?
|
|
(qse_awk_val_map_t*)qse_awk_val_nil:
|
|
(qse_awk_val_map_t*)QSE_HTB_VPTR(pair);
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
map = (qse_awk_val_map_t*)RTX_STACK_GBL(rtx,var->id.idxa);
|
|
break;
|
|
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
map = (qse_awk_val_map_t*)RTX_STACK_LCL(rtx,var->id.idxa);
|
|
break;
|
|
|
|
default: /* QSE_AWK_NDE_ARGIDX */
|
|
map = (qse_awk_val_map_t*)RTX_STACK_ARG(rtx,var->id.idxa);
|
|
break;
|
|
}
|
|
|
|
mvtype = QSE_AWK_RTX_GETVALTYPE (rtx, map);
|
|
if (mvtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
|
|
/* the map is not initialized yet */
|
|
tmp = qse_awk_rtx_makemapval (rtx);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
switch (var->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMEDIDX:
|
|
{
|
|
/* doesn't have to decrease the reference count
|
|
* of the previous value here as it is done by
|
|
* qse_htb_upsert */
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
if (qse_htb_upsert(rtx->named, var->id.name.ptr, var->id.name.len, tmp, 0) == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
{
|
|
int x;
|
|
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
x = qse_awk_rtx_setgbl (rtx, (int)var->id.idxa, tmp);
|
|
qse_awk_rtx_refdownval (rtx, tmp);
|
|
if (x <= -1)
|
|
{
|
|
ADJERR_LOC (rtx, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
qse_awk_rtx_refdownval (rtx, (qse_awk_val_t*)map);
|
|
RTX_STACK_LCL(rtx,var->id.idxa) = tmp;
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
break;
|
|
|
|
default: /* QSE_AWK_NDE_ARGIDX */
|
|
qse_awk_rtx_refdownval (rtx, (qse_awk_val_t*)map);
|
|
RTX_STACK_ARG(rtx,var->id.idxa) = tmp;
|
|
qse_awk_rtx_refupval (rtx, tmp);
|
|
break;
|
|
}
|
|
|
|
map = (qse_awk_val_map_t*)tmp;
|
|
}
|
|
else if (mvtype != QSE_AWK_VAL_MAP)
|
|
{
|
|
if (rtx->awk->opt.trait & QSE_AWK_FLEXMAP)
|
|
{
|
|
/* if FLEXMAP is on, you can switch a scalar value to a map */
|
|
qse_awk_nde_var_t fake;
|
|
|
|
/* create a fake non-indexed variable node */
|
|
fake = *var;
|
|
/* NOTE: type conversion by decrementing by 4 is
|
|
* dependent on the qse_awk_nde_type_t
|
|
* enumerators defined in <qse/awk/awk.h> */
|
|
fake.type = var->type - 4;
|
|
fake.idx = QSE_NULL;
|
|
|
|
reset_variable (rtx, &fake);
|
|
goto retry;
|
|
}
|
|
else
|
|
{
|
|
/* you can't manipulate a variable pointing to
|
|
* a scalar value with an index if FLEXMAP is off. */
|
|
SETERR_LOC (rtx, QSE_AWK_ESCALARTOMAP, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
len = QSE_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, var->idx, idxbuf, &len);
|
|
if (str == QSE_NULL) return QSE_NULL;
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("**** index str=>%s, map->ref=%d, map->type=%d\n"),
|
|
str, (int)map->ref, (int)map->type);
|
|
#endif
|
|
|
|
if (qse_htb_upsert(map->map, str, len, val, 0) == QSE_NULL)
|
|
{
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &var->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
qse_awk_rtx_refupval (rtx, val);
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* do_assignment_pos (qse_awk_rtx_t* rtx, qse_awk_nde_pos_t* pos, qse_awk_val_t* val)
|
|
{
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
qse_awk_int_t lv;
|
|
qse_cstr_t str;
|
|
int n;
|
|
|
|
v = eval_expression (rtx, pos->val);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint (rtx, v, &lv);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
if (n <= -1)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EPOSIDX, &pos->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (!IS_VALID_POSIDX(lv))
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EPOSIDX, &pos->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, val);
|
|
if (vtype == QSE_AWK_VAL_STR)
|
|
{
|
|
str = ((qse_awk_val_str_t*)val)->val;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr (rtx, val, &out) <= -1)
|
|
{
|
|
ADJERR_LOC (rtx, &pos->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str = out.u.cpldup;
|
|
}
|
|
|
|
n = qse_awk_rtx_setrec (rtx, (qse_size_t)lv, &str);
|
|
|
|
if (vtype == QSE_AWK_VAL_STR)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else
|
|
{
|
|
qse_awk_rtx_freemem (rtx, str.ptr);
|
|
}
|
|
|
|
if (n <= -1) return QSE_NULL;
|
|
return (lv == 0)? rtx->inrec.d0: rtx->inrec.flds[lv-1].val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binary (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
|
|
{
|
|
static binop_func_t binop_func[] =
|
|
{
|
|
/* the order of the functions should be inline with
|
|
* the operator declaration in run.h */
|
|
|
|
QSE_NULL, /* eval_binop_lor */
|
|
QSE_NULL, /* eval_binop_land */
|
|
QSE_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,
|
|
QSE_NULL, /* eval_binop_ma */
|
|
QSE_NULL /* eval_binop_nm */
|
|
};
|
|
|
|
qse_awk_nde_exp_t* exp = (qse_awk_nde_exp_t*)nde;
|
|
qse_awk_val_t* left, * right, * res;
|
|
|
|
QSE_ASSERT (exp->type == QSE_AWK_NDE_EXP_BIN);
|
|
|
|
if (exp->opcode == QSE_AWK_BINOP_LAND)
|
|
{
|
|
res = eval_binop_land (run, exp->left, exp->right);
|
|
}
|
|
else if (exp->opcode == QSE_AWK_BINOP_LOR)
|
|
{
|
|
res = eval_binop_lor (run, exp->left, exp->right);
|
|
}
|
|
else if (exp->opcode == QSE_AWK_BINOP_IN)
|
|
{
|
|
/* treat the in operator specially */
|
|
res = eval_binop_in (run, exp->left, exp->right);
|
|
}
|
|
else if (exp->opcode == QSE_AWK_BINOP_NM)
|
|
{
|
|
res = eval_binop_nm (run, exp->left, exp->right);
|
|
}
|
|
else if (exp->opcode == QSE_AWK_BINOP_MA)
|
|
{
|
|
res = eval_binop_ma (run, exp->left, exp->right);
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (exp->left->next == QSE_NULL);
|
|
left = eval_expression (run, exp->left);
|
|
if (left == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, left);
|
|
|
|
QSE_ASSERT (exp->right->next == QSE_NULL);
|
|
right = eval_expression (run, exp->right);
|
|
if (right == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (run, left);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (run, right);
|
|
|
|
QSE_ASSERT (exp->opcode >= 0 &&
|
|
exp->opcode < QSE_COUNTOF(binop_func));
|
|
QSE_ASSERT (binop_func[exp->opcode] != QSE_NULL);
|
|
|
|
res = binop_func[exp->opcode] (run, left, right);
|
|
if (res == QSE_NULL) ADJERR_LOC (run, &nde->loc);
|
|
|
|
qse_awk_rtx_refdownval (run, left);
|
|
qse_awk_rtx_refdownval (run, right);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_lor (
|
|
qse_awk_rtx_t* run, qse_awk_nde_t* left, qse_awk_nde_t* right)
|
|
{
|
|
/*
|
|
qse_awk_val_t* res = QSE_NULL;
|
|
|
|
res = qse_awk_rtx_makeintval (
|
|
run,
|
|
qse_awk_rtx_valtobool(run,left) ||
|
|
qse_awk_rtx_valtobool(run,right)
|
|
);
|
|
if (res == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (run, &left->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return res;
|
|
*/
|
|
|
|
/* short-circuit evaluation required special treatment */
|
|
qse_awk_val_t* lv, * rv, * res;
|
|
|
|
QSE_ASSERT (left->next == QSE_NULL);
|
|
lv = eval_expression (run, left);
|
|
if (lv == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, lv);
|
|
if (qse_awk_rtx_valtobool(run, lv))
|
|
{
|
|
res = QSE_AWK_VAL_ONE;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (right->next == QSE_NULL);
|
|
rv = eval_expression (run, right);
|
|
if (rv == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
return QSE_NULL;
|
|
}
|
|
qse_awk_rtx_refupval (run, rv);
|
|
|
|
res = qse_awk_rtx_valtobool(run,rv)?
|
|
QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
qse_awk_rtx_refdownval (run, rv);
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_land (qse_awk_rtx_t* run, qse_awk_nde_t* left, qse_awk_nde_t* right)
|
|
{
|
|
/*
|
|
qse_awk_val_t* res = QSE_NULL;
|
|
|
|
res = qse_awk_rtx_makeintval (
|
|
run,
|
|
qse_awk_rtx_valtobool(run,left) &&
|
|
qse_awk_rtx_valtobool(run,right)
|
|
);
|
|
if (res == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (run, &left->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return res;
|
|
*/
|
|
|
|
/* short-circuit evaluation required special treatment */
|
|
qse_awk_val_t* lv, * rv, * res;
|
|
|
|
QSE_ASSERT (left->next == QSE_NULL);
|
|
lv = eval_expression (run, left);
|
|
if (lv == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, lv);
|
|
if (!qse_awk_rtx_valtobool(run, lv))
|
|
{
|
|
res = QSE_AWK_VAL_ZERO;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (right->next == QSE_NULL);
|
|
rv = eval_expression (run, right);
|
|
if (rv == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
return QSE_NULL;
|
|
}
|
|
qse_awk_rtx_refupval (run, rv);
|
|
|
|
res = qse_awk_rtx_valtobool(run,rv)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
qse_awk_rtx_refdownval (run, rv);
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_in (qse_awk_rtx_t* rtx, qse_awk_nde_t* left, qse_awk_nde_t* right)
|
|
{
|
|
qse_awk_val_t* rv;
|
|
qse_awk_val_type_t rvtype;
|
|
qse_char_t* str;
|
|
qse_size_t len;
|
|
qse_char_t idxbuf[IDXBUFSIZE];
|
|
|
|
if (right->type != QSE_AWK_NDE_GBL &&
|
|
right->type != QSE_AWK_NDE_LCL &&
|
|
right->type != QSE_AWK_NDE_ARG &&
|
|
right->type != QSE_AWK_NDE_NAMED)
|
|
{
|
|
/* the compiler should have handled this case */
|
|
QSE_ASSERT (!"should never happen - it needs a plain variable");
|
|
SETERR_LOC (rtx, QSE_AWK_EINTERN, &right->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* evaluate the left-hand side of the operator */
|
|
len = QSE_COUNTOF(idxbuf);
|
|
str = (left->type == QSE_AWK_NDE_GRP)?
|
|
idxnde_to_str (rtx, ((qse_awk_nde_grp_t*)left)->body, idxbuf, &len):
|
|
idxnde_to_str (rtx, left, idxbuf, &len);
|
|
if (str == QSE_NULL) return QSE_NULL;
|
|
|
|
/* evaluate the right-hand side of the operator */
|
|
QSE_ASSERT (right->next == QSE_NULL);
|
|
rv = eval_expression (rtx, right);
|
|
if (rv == QSE_NULL)
|
|
{
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, rv);
|
|
|
|
rvtype = QSE_AWK_RTX_GETVALTYPE (rtx, rv);
|
|
if (rvtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
return QSE_AWK_VAL_ZERO;
|
|
}
|
|
else if (rvtype == QSE_AWK_VAL_MAP)
|
|
{
|
|
qse_awk_val_t* res;
|
|
qse_htb_t* map;
|
|
|
|
map = ((qse_awk_val_map_t*)rv)->map;
|
|
res = (qse_htb_search (map, str, len) == QSE_NULL)?
|
|
QSE_AWK_VAL_ZERO: QSE_AWK_VAL_ONE;
|
|
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
return res;
|
|
}
|
|
|
|
/* need a map */
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
qse_awk_rtx_refdownval (rtx, rv);
|
|
|
|
SETERR_LOC (rtx, QSE_AWK_ENOTMAPNILIN, &right->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_bor (
|
|
qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_int_t l1, l2;
|
|
|
|
if (qse_awk_rtx_valtoint (rtx, left, &l1) <= -1 ||
|
|
qse_awk_rtx_valtoint (rtx, right, &l2) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return qse_awk_rtx_makeintval (rtx, l1 | l2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_bxor (
|
|
qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_int_t l1, l2;
|
|
|
|
if (qse_awk_rtx_valtoint (rtx, left, &l1) <= -1 ||
|
|
qse_awk_rtx_valtoint (rtx, right, &l2) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return qse_awk_rtx_makeintval (rtx, l1 ^ l2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_band (
|
|
qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_int_t l1, l2;
|
|
|
|
if (qse_awk_rtx_valtoint (rtx, left, &l1) <= -1 ||
|
|
qse_awk_rtx_valtoint (rtx, right, &l2) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return qse_awk_rtx_makeintval (rtx, l1 & l2);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
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;
|
|
|
|
static QSE_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 QSE_INLINE int __cmp_ensure_not_equal (qse_awk_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:
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
static QSE_INLINE int __cmp_nil_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_int_t v = QSE_AWK_RTX_GETINTFROMVAL (rtx, right);
|
|
return (v < 0)? 1: ((v > 0)? -1: 0);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((qse_awk_val_flt_t*)right)->val < 0) return 1;
|
|
if (((qse_awk_val_flt_t*)right)->val > 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((qse_awk_val_str_t*)right)->val.len == 0)? 0: -1;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((qse_awk_val_mbs_t*)right)->val.len == 0)? 0: -1;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* != -> true, all others -> false */
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_nil_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (QSE_HTB_SIZE(((qse_awk_val_map_t*)right)->map) == 0)? 0: -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_int_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_int_t v = QSE_AWK_RTX_GETINTFROMVAL(rtx, left);
|
|
return (v > 0)? 1: ((v < 0)? -1: 0);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
|
|
qse_awk_int_t v1 = QSE_AWK_RTX_GETINTFROMVAL(rtx, left);
|
|
qse_awk_int_t v2 = QSE_AWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_flt (
|
|
qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_int_t v1 = QSE_AWK_RTX_GETINTFROMVAL (rtx, left);
|
|
if (v1 > ((qse_awk_val_flt_t*)right)->val) return 1;
|
|
if (v1 < ((qse_awk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_char_t* str0;
|
|
qse_size_t len0;
|
|
int n;
|
|
|
|
/* SCO CC doesn't seem to handle right->nstr > 0 properly */
|
|
if ((rtx->awk->opt.trait & QSE_AWK_NCMPONSTR) || right->nstr /*> 0*/)
|
|
{
|
|
qse_awk_int_t ll, v1;
|
|
qse_awk_flt_t rr;
|
|
|
|
n = qse_awk_rtx_strtonum (
|
|
rtx,
|
|
QSE_AWK_RTX_STRTONUM_MAKE_OPTION(1, 0),
|
|
((qse_awk_val_str_t*)right)->val.ptr,
|
|
((qse_awk_val_str_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
v1 = QSE_AWK_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 = qse_awk_rtx_valtostrdup(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
|
|
if (rtx->gbl.ignorecase)
|
|
{
|
|
n = qse_strxncasecmp(str0, len0, ((qse_awk_val_str_t*)right)->val.ptr, ((qse_awk_val_str_t*)right)->val.len);
|
|
}
|
|
else
|
|
{
|
|
n = qse_strxncmp(str0, len0, ((qse_awk_val_str_t*)right)->val.ptr, ((qse_awk_val_str_t*)right)->val.len);
|
|
}
|
|
|
|
qse_awk_rtx_freemem (rtx, str0);
|
|
return n;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_mchar_t* str0;
|
|
qse_size_t len0;
|
|
int n;
|
|
|
|
if ((rtx->awk->opt.trait & QSE_AWK_NCMPONSTR) || right->nstr /*> 0*/)
|
|
{
|
|
qse_awk_int_t ll, v1;
|
|
qse_awk_flt_t rr;
|
|
|
|
n = qse_awk_rtx_mbstonum (
|
|
rtx,
|
|
QSE_AWK_RTX_STRTONUM_MAKE_OPTION(1, 0),
|
|
((qse_awk_val_mbs_t*)right)->val.ptr,
|
|
((qse_awk_val_mbs_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
v1 = QSE_AWK_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 = qse_awk_rtx_valtombsdup(rtx, left, &len0);
|
|
if (!str0) return -1;
|
|
|
|
if (rtx->gbl.ignorecase)
|
|
{
|
|
n = qse_mbsxncasecmp(str0, len0, ((qse_awk_val_mbs_t*)right)->val.ptr, ((qse_awk_val_mbs_t*)right)->val.len);
|
|
}
|
|
else
|
|
{
|
|
n = qse_mbsxncmp(str0, len0, ((qse_awk_val_mbs_t*)right)->val.ptr, ((qse_awk_val_mbs_t*)right)->val.len);
|
|
}
|
|
|
|
qse_awk_rtx_freemem (rtx, str0);
|
|
return n;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_int_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* compare an integer and the size of a map */
|
|
qse_awk_int_t v1 = QSE_AWK_RTX_GETINTFROMVAL(rtx, left);
|
|
qse_awk_int_t v2 = QSE_HTB_SIZE(((qse_awk_val_map_t*)right)->map);
|
|
if (v1 > v2) return 1;
|
|
if (v1 < v2) return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_flt_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((qse_awk_val_flt_t*)left)->val > 0) return 1;
|
|
if (((qse_awk_val_flt_t*)left)->val < 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_int_t v2 = QSE_AWK_RTX_GETINTFROMVAL (rtx, right);
|
|
if (((qse_awk_val_flt_t*)left)->val > v2) return 1;
|
|
if (((qse_awk_val_flt_t*)left)->val < v2) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((qse_awk_val_flt_t*)left)->val >
|
|
((qse_awk_val_flt_t*)right)->val) return 1;
|
|
if (((qse_awk_val_flt_t*)left)->val <
|
|
((qse_awk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_char_t* str0;
|
|
qse_size_t len0;
|
|
int n;
|
|
|
|
/* SCO CC doesn't seem to handle right->nstr > 0 properly */
|
|
if ((rtx->awk->opt.trait & QSE_AWK_NCMPONSTR) || right->nstr /*> 0*/)
|
|
{
|
|
const qse_char_t* end;
|
|
qse_awk_flt_t rr;
|
|
|
|
rr = qse_awk_strxtoflt(rtx->awk, ((qse_awk_val_str_t*)right)->val.ptr, ((qse_awk_val_str_t*)right)->val.len, &end);
|
|
if (end == ((qse_awk_val_str_t*)right)->val.ptr + ((qse_awk_val_str_t*)right)->val.len)
|
|
{
|
|
return (((qse_awk_val_flt_t*)left)->val > rr)? 1:
|
|
(((qse_awk_val_flt_t*)left)->val < rr)? -1: 0;
|
|
}
|
|
}
|
|
|
|
str0 = qse_awk_rtx_valtostrdup(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
|
|
if (rtx->gbl.ignorecase)
|
|
{
|
|
n = qse_strxncasecmp(str0, len0, ((qse_awk_val_str_t*)right)->val.ptr, ((qse_awk_val_str_t*)right)->val.len);
|
|
}
|
|
else
|
|
{
|
|
n = qse_strxncmp(str0, len0, ((qse_awk_val_str_t*)right)->val.ptr, ((qse_awk_val_str_t*)right)->val.len);
|
|
}
|
|
|
|
qse_awk_rtx_freemem (rtx, str0);
|
|
return n;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_mchar_t* str0;
|
|
qse_size_t len0;
|
|
int n;
|
|
|
|
if ((rtx->awk->opt.trait & QSE_AWK_NCMPONSTR) || right->nstr /*> 0*/)
|
|
{
|
|
const qse_mchar_t* end;
|
|
qse_awk_flt_t rr;
|
|
|
|
rr = qse_awk_mbsxtoflt(rtx->awk, ((qse_awk_val_mbs_t*)right)->val.ptr, ((qse_awk_val_mbs_t*)right)->val.len, &end);
|
|
if (end == ((qse_awk_val_mbs_t*)right)->val.ptr + ((qse_awk_val_mbs_t*)right)->val.len)
|
|
{
|
|
return (((qse_awk_val_flt_t*)left)->val > rr)? 1:
|
|
(((qse_awk_val_flt_t*)left)->val < rr)? -1: 0;
|
|
}
|
|
}
|
|
|
|
str0 = qse_awk_rtx_valtombsdup(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
|
|
if (rtx->gbl.ignorecase)
|
|
{
|
|
n = qse_mbsxncasecmp(str0, len0, ((qse_awk_val_mbs_t*)right)->val.ptr, ((qse_awk_val_mbs_t*)right)->val.len);
|
|
}
|
|
else
|
|
{
|
|
n = qse_mbsxncmp(str0, len0, ((qse_awk_val_mbs_t*)right)->val.ptr, ((qse_awk_val_mbs_t*)right)->val.len);
|
|
}
|
|
|
|
qse_awk_rtx_freemem (rtx, str0);
|
|
return n;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_flt_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* compare a float with the size of a map */
|
|
qse_awk_int_t v2 = QSE_HTB_SIZE(((qse_awk_val_map_t*)right)->map);
|
|
if (((qse_awk_val_flt_t*)left)->val > v2) return 1;
|
|
if (((qse_awk_val_flt_t*)left)->val < v2) return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_str_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((qse_awk_val_str_t*)left)->val.len == 0)? 0: 1;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_str_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_str_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_str_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_val_str_t* ls, * rs;
|
|
|
|
ls = (qse_awk_val_str_t*)left;
|
|
rs = (qse_awk_val_str_t*)right;
|
|
|
|
if (ls->nstr == 0 || rs->nstr == 0)
|
|
{
|
|
/* both are definitely strings */
|
|
return (rtx->gbl.ignorecase)?
|
|
qse_strxncasecmp(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len):
|
|
qse_strxncmp(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len);
|
|
}
|
|
|
|
if (ls->nstr == 1)
|
|
{
|
|
qse_awk_int_t ll;
|
|
|
|
ll = qse_awk_strxtoint(rtx->awk, ls->val.ptr, ls->val.len, 0, QSE_NULL);
|
|
|
|
if (rs->nstr == 1)
|
|
{
|
|
qse_awk_int_t rr;
|
|
|
|
rr = qse_awk_strxtoint(rtx->awk, rs->val.ptr, rs->val.len, 0, QSE_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_flt_t rr;
|
|
|
|
QSE_ASSERT (rs->nstr == 2);
|
|
|
|
rr = qse_awk_strxtoflt(rtx->awk, rs->val.ptr, rs->val.len, QSE_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qse_awk_flt_t ll;
|
|
|
|
QSE_ASSERT (ls->nstr == 2);
|
|
|
|
ll = qse_awk_strxtoflt(rtx->awk, ls->val.ptr, ls->val.len, QSE_NULL);
|
|
|
|
if (rs->nstr == 1)
|
|
{
|
|
qse_awk_int_t rr;
|
|
|
|
rr = qse_awk_strxtoint(rtx->awk, rs->val.ptr, rs->val.len, 0, QSE_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_flt_t rr;
|
|
|
|
QSE_ASSERT (rs->nstr == 2);
|
|
|
|
rr = qse_awk_strxtoflt(rtx->awk, rs->val.ptr, rs->val.len, QSE_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_str_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_val_str_t* ls = (qse_awk_val_str_t*)left;
|
|
qse_awk_val_mbs_t* rs = (qse_awk_val_mbs_t*)right;
|
|
|
|
#if (QSE_SIZEOF_MCHAR_T != QSE_SIZEOF_UINT8_T)
|
|
# error Unsupported size of qse_mchar_t
|
|
#endif
|
|
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
return qse_mbsxncmp(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len);
|
|
#else
|
|
qse_mchar_t* mbsptr;
|
|
qse_size_t mbslen;
|
|
int n;
|
|
|
|
mbsptr = qse_wcsntombsdupwithcmgr(ls->val.ptr, ls->val.len, &mbslen, qse_awk_rtx_getmmgr(rtx), qse_awk_rtx_getcmgr(rtx));
|
|
if (!mbsptr)
|
|
{
|
|
qse_awk_rtx_seterrnum (rtx, QSE_AWK_ENOMEM, QSE_NULL);
|
|
return CMP_ERROR;
|
|
}
|
|
n = qse_mbsxncmp(mbsptr, mbslen, (const qse_mchar_t*)rs->val.ptr, rs->val.len);
|
|
qse_awk_rtx_freemem (rtx, mbsptr);
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_str_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_str_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_mbs_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((qse_awk_val_mbs_t*)left)->val.len == 0)? 0: 1;
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_mbs_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_mbs_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_mbs_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_mbs_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
qse_awk_val_mbs_t* ls = (qse_awk_val_mbs_t*)left;
|
|
qse_awk_val_mbs_t* rs = (qse_awk_val_mbs_t*)right;
|
|
#if (QSE_SIZEOF_MCHAR_T != QSE_SIZEOF_UINT8_T)
|
|
# error Unsupported size of qse_mchar_t
|
|
#endif
|
|
return qse_mbsxncmp(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_mbs_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_mbs_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_fun_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((qse_awk_val_fun_t*)left)->fun == ((qse_awk_val_fun_t*)right)->fun)? 0: __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_fun_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static QSE_INLINE int __cmp_map_nil (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_map_int (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_map_flt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_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 QSE_INLINE int __cmp_map_str (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_map_mbs (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_map_fun (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static QSE_INLINE int __cmp_map_map (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* can't compare a map with a map */
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return CMP_ERROR;
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
static QSE_INLINE int __cmp_val (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, cmp_op_t op_hint, int* ret)
|
|
{
|
|
int n;
|
|
qse_awk_val_type_t lvtype, rvtype;
|
|
typedef int (*cmp_val_t) (qse_awk_rtx_t*, qse_awk_val_t*, qse_awk_val_t*, cmp_op_t op_hint);
|
|
|
|
static cmp_val_t func[] =
|
|
{
|
|
/* this table must be synchronized with
|
|
* the QSE_AWK_VAL_XXX values in awk.h */
|
|
__cmp_nil_nil, __cmp_nil_int, __cmp_nil_flt, __cmp_nil_str, __cmp_nil_mbs, __cmp_nil_fun, __cmp_nil_map,
|
|
__cmp_int_nil, __cmp_int_int, __cmp_int_flt, __cmp_int_str, __cmp_int_mbs, __cmp_int_fun, __cmp_int_map,
|
|
__cmp_flt_nil, __cmp_flt_int, __cmp_flt_flt, __cmp_flt_str, __cmp_flt_mbs, __cmp_flt_fun, __cmp_flt_map,
|
|
__cmp_str_nil, __cmp_str_int, __cmp_str_flt, __cmp_str_str, __cmp_str_mbs, __cmp_str_fun, __cmp_str_map,
|
|
__cmp_mbs_nil, __cmp_mbs_int, __cmp_mbs_flt, __cmp_mbs_str, __cmp_mbs_mbs, __cmp_mbs_fun, __cmp_mbs_map,
|
|
__cmp_fun_nil, __cmp_fun_int, __cmp_fun_flt, __cmp_fun_str, __cmp_fun_mbs, __cmp_fun_fun, __cmp_fun_map,
|
|
__cmp_map_nil, __cmp_map_int, __cmp_map_flt, __cmp_map_str, __cmp_map_mbs, __cmp_map_fun, __cmp_map_map
|
|
};
|
|
|
|
lvtype = QSE_AWK_RTX_GETVALTYPE(rtx, left);
|
|
rvtype = QSE_AWK_RTX_GETVALTYPE(rtx, right);
|
|
if (!(rtx->awk->opt.trait & QSE_AWK_FLEXMAP) &&
|
|
(lvtype == QSE_AWK_VAL_MAP || rvtype == QSE_AWK_VAL_MAP))
|
|
{
|
|
/* a map can't be compared againt other values */
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return -1;
|
|
}
|
|
|
|
QSE_ASSERT (lvtype >= QSE_AWK_VAL_NIL && lvtype <= QSE_AWK_VAL_MAP);
|
|
QSE_ASSERT (rvtype >= QSE_AWK_VAL_NIL && rvtype <= QSE_AWK_VAL_MAP);
|
|
|
|
/* mapping fomula and table layout assume:
|
|
* QSE_AWK_VAL_NIL = 0
|
|
* QSE_AWK_VAL_INT = 1
|
|
* QSE_AWK_VAL_FLT = 2
|
|
* QSE_AWK_VAL_STR = 3
|
|
* QSE_AWK_VAL_MBS = 4
|
|
* QSE_AWK_VAL_FUN = 5
|
|
* QSE_AWK_VAL_MAP = 6
|
|
*
|
|
* 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 * 7 + rvtype](rtx, left, right, op_hint);
|
|
if (n == CMP_ERROR) return -1;
|
|
|
|
*ret = n;
|
|
return 0;
|
|
}
|
|
|
|
int qse_awk_rtx_cmpval (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right, int* ret)
|
|
{
|
|
return __cmp_val(rtx, left, right, CMP_OP_NONE, ret);
|
|
}
|
|
|
|
static int teq_val (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
qse_awk_val_type_t lt, rt;
|
|
|
|
if (left == right) n = 1;
|
|
else
|
|
{
|
|
lt = QSE_AWK_RTX_GETVALTYPE(rtx, left);
|
|
rt = QSE_AWK_RTX_GETVALTYPE(rtx, right);
|
|
|
|
if (lt != rt) n = 0;
|
|
else
|
|
{
|
|
switch (lt)
|
|
{
|
|
case QSE_AWK_VAL_NIL:
|
|
n = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_INT:
|
|
n = (QSE_AWK_RTX_GETINTFROMVAL (rtx, left) == QSE_AWK_RTX_GETINTFROMVAL (rtx, right));
|
|
break;
|
|
|
|
case QSE_AWK_VAL_FLT:
|
|
n = ((qse_awk_val_flt_t*)left)->val == ((qse_awk_val_flt_t*)right)->val;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_STR:
|
|
n = qse_strxncmp (
|
|
((qse_awk_val_str_t*)left)->val.ptr,
|
|
((qse_awk_val_str_t*)left)->val.len,
|
|
((qse_awk_val_str_t*)right)->val.ptr,
|
|
((qse_awk_val_str_t*)right)->val.len) == 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
n = qse_mbsxncmp (
|
|
((qse_awk_val_mbs_t*)left)->val.ptr,
|
|
((qse_awk_val_mbs_t*)left)->val.len,
|
|
((qse_awk_val_mbs_t*)right)->val.ptr,
|
|
((qse_awk_val_mbs_t*)right)->val.len) == 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_FUN:
|
|
n = ((qse_awk_val_fun_t*)left)->fun == ((qse_awk_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 qse_awk_val_t* eval_binop_teq (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
return teq_val(rtx, left, right)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_tne (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
return teq_val(rtx, left, right)? QSE_AWK_VAL_ZERO: QSE_AWK_VAL_ONE;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_eq (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_EQ, &n) <= -1) return QSE_NULL;
|
|
return (n == 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_ne (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_NE, &n) <= -1) return QSE_NULL;
|
|
return (n != 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_gt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_GT, &n) <= -1) return QSE_NULL;
|
|
return (n > 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_ge (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_GE, &n) <= -1) return QSE_NULL;
|
|
return (n >= 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_lt (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_LT, &n) <= -1) return QSE_NULL;
|
|
return (n < 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_le (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n;
|
|
if (__cmp_val(rtx, left, right, CMP_OP_LE, &n) <= -1) return QSE_NULL;
|
|
return (n <= 0)? QSE_AWK_VAL_ONE: QSE_AWK_VAL_ZERO;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_lshift (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_int_t l1, l2;
|
|
|
|
if (qse_awk_rtx_valtoint(rtx, left, &l1) <= -1 ||
|
|
qse_awk_rtx_valtoint(rtx, right, &l2) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return qse_awk_rtx_makeintval(rtx, l1 << l2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_rshift (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_int_t l1, l2;
|
|
|
|
if (qse_awk_rtx_valtoint(rtx, left, &l1) <= -1 ||
|
|
qse_awk_rtx_valtoint(rtx, right, &l2) <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return qse_awk_rtx_makeintval (rtx, l1 >> l2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_plus (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
|
|
n1 = qse_awk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
/*
|
|
n1 n2 n3
|
|
0 0 = 0
|
|
1 0 = 1
|
|
0 1 = 2
|
|
1 1 = 3
|
|
*/
|
|
n3 = n1 + (n2 << 1);
|
|
QSE_ASSERT (n3 >= 0 && n3 <= 3);
|
|
|
|
return (n3 == 0)? qse_awk_rtx_makeintval(rtx,(qse_awk_int_t)l1+(qse_awk_int_t)l2):
|
|
(n3 == 1)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1+(qse_awk_flt_t)l2):
|
|
(n3 == 2)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)l1+(qse_awk_flt_t)r2):
|
|
qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1+(qse_awk_flt_t)r2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_minus (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
|
|
n1 = qse_awk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
QSE_ASSERT (n3 >= 0 && n3 <= 3);
|
|
return (n3 == 0)? qse_awk_rtx_makeintval(rtx,(qse_awk_int_t)l1-(qse_awk_int_t)l2):
|
|
(n3 == 1)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1-(qse_awk_flt_t)l2):
|
|
(n3 == 2)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)l1-(qse_awk_flt_t)r2):
|
|
qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1-(qse_awk_flt_t)r2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_mul (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
|
|
n1 = qse_awk_rtx_valtonum (rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum (rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
QSE_ASSERT (n3 >= 0 && n3 <= 3);
|
|
return (n3 == 0)? qse_awk_rtx_makeintval(rtx,(qse_awk_int_t)l1*(qse_awk_int_t)l2):
|
|
(n3 == 1)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1*(qse_awk_flt_t)l2):
|
|
(n3 == 2)? qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)l1*(qse_awk_flt_t)r2):
|
|
qse_awk_rtx_makefltval(rtx,(qse_awk_flt_t)r1*(qse_awk_flt_t)r2);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_div (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
qse_awk_val_t* res;
|
|
|
|
n1 = qse_awk_rtx_valtonum (rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum (rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EDIVBY0);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (((qse_awk_int_t)l1 % (qse_awk_int_t)l2) == 0)
|
|
{
|
|
res = qse_awk_rtx_makeintval (
|
|
rtx, (qse_awk_int_t)l1 / (qse_awk_int_t)l2);
|
|
}
|
|
else
|
|
{
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx, (qse_awk_flt_t)l1 / (qse_awk_flt_t)l2);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx, (qse_awk_flt_t)r1 / (qse_awk_flt_t)l2);
|
|
break;
|
|
|
|
case 2:
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx, (qse_awk_flt_t)l1 / (qse_awk_flt_t)r2);
|
|
break;
|
|
|
|
case 3:
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx, (qse_awk_flt_t)r1 / (qse_awk_flt_t)r2);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_idiv (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2, quo;
|
|
qse_awk_val_t* res;
|
|
|
|
n1 = qse_awk_rtx_valtonum (rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum (rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EDIVBY0);
|
|
return QSE_NULL;
|
|
}
|
|
res = qse_awk_rtx_makeintval (
|
|
rtx, (qse_awk_int_t)l1 / (qse_awk_int_t)l2);
|
|
break;
|
|
|
|
case 1:
|
|
quo = (qse_awk_flt_t)r1 / (qse_awk_flt_t)l2;
|
|
res = qse_awk_rtx_makeintval (rtx, (qse_awk_int_t)quo);
|
|
break;
|
|
|
|
case 2:
|
|
quo = (qse_awk_flt_t)l1 / (qse_awk_flt_t)r2;
|
|
res = qse_awk_rtx_makeintval (rtx, (qse_awk_int_t)quo);
|
|
break;
|
|
|
|
case 3:
|
|
quo = (qse_awk_flt_t)r1 / (qse_awk_flt_t)r2;
|
|
res = qse_awk_rtx_makeintval (rtx, (qse_awk_int_t)quo);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_mod (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
qse_awk_val_t* res;
|
|
|
|
QSE_ASSERTX (rtx->awk->prm.math.mod != QSE_NULL,
|
|
"the mod function must be provided when the awk object is created to be able to calculate floating-pointer remainder.");
|
|
|
|
n1 = qse_awk_rtx_valtonum (rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum (rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EDIVBY0);
|
|
return QSE_NULL;
|
|
}
|
|
res = qse_awk_rtx_makeintval (
|
|
rtx, (qse_awk_int_t)l1 % (qse_awk_int_t)l2);
|
|
break;
|
|
|
|
case 1:
|
|
res = qse_awk_rtx_makefltval (rtx,
|
|
rtx->awk->prm.math.mod (
|
|
rtx->awk, (qse_awk_flt_t)r1, (qse_awk_flt_t)l2)
|
|
);
|
|
break;
|
|
|
|
case 2:
|
|
res = qse_awk_rtx_makefltval (rtx,
|
|
rtx->awk->prm.math.mod (
|
|
rtx->awk, (qse_awk_flt_t)l1, (qse_awk_flt_t)r2)
|
|
);
|
|
break;
|
|
|
|
case 3:
|
|
res = qse_awk_rtx_makefltval (rtx,
|
|
rtx->awk->prm.math.mod (
|
|
rtx->awk, (qse_awk_flt_t)r1, (qse_awk_flt_t)r2)
|
|
);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_exp (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
qse_awk_int_t l1, l2;
|
|
qse_awk_flt_t r1, r2;
|
|
qse_awk_val_t* res;
|
|
|
|
n1 = qse_awk_rtx_valtonum (rtx, left, &l1, &r1);
|
|
n2 = qse_awk_rtx_valtonum (rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EOPERAND);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
/* left - int, right - int */
|
|
if (l2 >= 0)
|
|
{
|
|
qse_awk_int_t v = 1;
|
|
while (l2-- > 0) v *= l1;
|
|
res = qse_awk_rtx_makeintval (rtx, v);
|
|
}
|
|
else if (l1 == 0)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EDIVBY0);
|
|
return QSE_NULL;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_flt_t v = 1.0;
|
|
l2 *= -1;
|
|
while (l2-- > 0) v /= l1;
|
|
res = qse_awk_rtx_makefltval (rtx, v);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
/* left - real, right - int */
|
|
if (l2 >= 0)
|
|
{
|
|
qse_awk_flt_t v = 1.0;
|
|
while (l2-- > 0) v *= r1;
|
|
res = qse_awk_rtx_makefltval (rtx, v);
|
|
}
|
|
else if (r1 == 0.0)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EDIVBY0);
|
|
return QSE_NULL;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_flt_t v = 1.0;
|
|
l2 *= -1;
|
|
while (l2-- > 0) v /= r1;
|
|
res = qse_awk_rtx_makefltval (rtx, v);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
/* left - int, right - real */
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx,
|
|
rtx->awk->prm.math.pow (
|
|
rtx->awk, (qse_awk_flt_t)l1, (qse_awk_flt_t)r2
|
|
)
|
|
);
|
|
break;
|
|
|
|
case 3:
|
|
/* left - real, right - real */
|
|
res = qse_awk_rtx_makefltval (
|
|
rtx,
|
|
rtx->awk->prm.math.pow (
|
|
rtx->awk, (qse_awk_flt_t)r1,(qse_awk_flt_t)r2
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_concat (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right)
|
|
{
|
|
qse_awk_val_t* res;
|
|
qse_awk_rtx_valtostr_out_t lout, rout;
|
|
|
|
lout.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, left, &lout) <= -1) return QSE_NULL;
|
|
|
|
rout.type = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, right, &rout) <= -1)
|
|
{
|
|
qse_awk_rtx_freemem (rtx, lout.u.cpldup.ptr);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res = qse_awk_rtx_makestrval2(
|
|
rtx,
|
|
lout.u.cpldup.ptr, lout.u.cpldup.len,
|
|
rout.u.cpldup.ptr, rout.u.cpldup.len
|
|
);
|
|
|
|
qse_awk_rtx_freemem (rtx, rout.u.cpldup.ptr);
|
|
qse_awk_rtx_freemem (rtx, lout.u.cpldup.ptr);
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_match0 (
|
|
qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right,
|
|
const qse_awk_loc_t* lloc, const qse_awk_loc_t* rloc, int ret)
|
|
{
|
|
qse_awk_val_t* res;
|
|
qse_cstr_t out;
|
|
int n;
|
|
|
|
out.ptr = qse_awk_rtx_getvalstr (rtx, left, &out.len);
|
|
if (out.ptr == QSE_NULL) return QSE_NULL;
|
|
|
|
n = qse_awk_rtx_matchrex (rtx, right, &out, &out, QSE_NULL, QSE_NULL);
|
|
qse_awk_rtx_freevalstr (rtx, left, out.ptr);
|
|
|
|
if (n <= -1)
|
|
{
|
|
ADJERR_LOC (rtx, lloc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res = qse_awk_rtx_makeintval (rtx, (n == ret));
|
|
if (res == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, lloc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_ma (qse_awk_rtx_t* run, qse_awk_nde_t* left, qse_awk_nde_t* right)
|
|
{
|
|
qse_awk_val_t* lv, * rv, * res;
|
|
|
|
QSE_ASSERT (left->next == QSE_NULL);
|
|
QSE_ASSERT (right->next == QSE_NULL);
|
|
|
|
lv = eval_expression (run, left);
|
|
if (lv == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, lv);
|
|
|
|
rv = eval_expression0 (run, right);
|
|
if (rv == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (run, rv);
|
|
|
|
res = eval_binop_match0 (run, lv, rv, &left->loc, &right->loc, 1);
|
|
|
|
qse_awk_rtx_refdownval (run, rv);
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_binop_nm (qse_awk_rtx_t* run, qse_awk_nde_t* left, qse_awk_nde_t* right)
|
|
{
|
|
qse_awk_val_t* lv, * rv, * res;
|
|
|
|
QSE_ASSERT (left->next == QSE_NULL);
|
|
QSE_ASSERT (right->next == QSE_NULL);
|
|
|
|
lv = eval_expression (run, left);
|
|
if (lv == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, lv);
|
|
|
|
rv = eval_expression0 (run, right);
|
|
if (rv == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (run, rv);
|
|
|
|
res = eval_binop_match0 (run, lv, rv, &left->loc, &right->loc, 0);
|
|
|
|
qse_awk_rtx_refdownval (run, rv);
|
|
qse_awk_rtx_refdownval (run, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_unary (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* left, * res = QSE_NULL;
|
|
qse_awk_nde_exp_t* exp = (qse_awk_nde_exp_t*)nde;
|
|
int n;
|
|
qse_awk_int_t l;
|
|
qse_awk_flt_t r;
|
|
|
|
QSE_ASSERT (
|
|
exp->type == QSE_AWK_NDE_EXP_UNR);
|
|
QSE_ASSERT (
|
|
exp->left != QSE_NULL && exp->right == QSE_NULL);
|
|
QSE_ASSERT (
|
|
exp->opcode == QSE_AWK_UNROP_PLUS ||
|
|
exp->opcode == QSE_AWK_UNROP_MINUS ||
|
|
exp->opcode == QSE_AWK_UNROP_LNOT ||
|
|
exp->opcode == QSE_AWK_UNROP_BNOT);
|
|
|
|
QSE_ASSERT (exp->left->next == QSE_NULL);
|
|
left = eval_expression (rtx, exp->left);
|
|
if (left == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, left);
|
|
|
|
switch (exp->opcode)
|
|
{
|
|
case QSE_AWK_UNROP_MINUS:
|
|
n = qse_awk_rtx_valtonum (rtx, left, &l, &r);
|
|
if (n <= -1) goto exit_func;
|
|
|
|
res = (n == 0)? qse_awk_rtx_makeintval (rtx, -l):
|
|
qse_awk_rtx_makefltval (rtx, -r);
|
|
break;
|
|
|
|
case QSE_AWK_UNROP_LNOT:
|
|
if (QSE_AWK_RTX_GETVALTYPE (rtx, left) == QSE_AWK_VAL_STR)
|
|
{
|
|
/* 0 if the string length is greater than 0.
|
|
* 1 if it's empty */
|
|
res = qse_awk_rtx_makeintval (
|
|
rtx, !(((qse_awk_val_str_t*)left)->val.len > 0));
|
|
}
|
|
else
|
|
{
|
|
n = qse_awk_rtx_valtonum (rtx, left, &l, &r);
|
|
if (n <= -1) goto exit_func;
|
|
|
|
res = (n == 0)? qse_awk_rtx_makeintval (rtx, !l):
|
|
qse_awk_rtx_makefltval (rtx, !r);
|
|
}
|
|
break;
|
|
|
|
case QSE_AWK_UNROP_BNOT:
|
|
n = qse_awk_rtx_valtoint (rtx, left, &l);
|
|
if (n <= -1) goto exit_func;
|
|
|
|
res = qse_awk_rtx_makeintval (rtx, ~l);
|
|
break;
|
|
|
|
case QSE_AWK_UNROP_PLUS:
|
|
n = qse_awk_rtx_valtonum (rtx, left, &l, &r);
|
|
if (n <= -1) goto exit_func;
|
|
|
|
res = (n == 0)? qse_awk_rtx_makeintval (rtx, l):
|
|
qse_awk_rtx_makefltval (rtx, r);
|
|
break;
|
|
}
|
|
|
|
exit_func:
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
if (res == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_incpre (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* left, * res;
|
|
qse_awk_val_type_t left_vtype;
|
|
qse_awk_int_t inc_val_int;
|
|
qse_awk_flt_t inc_val_flt;
|
|
qse_awk_nde_exp_t* exp = (qse_awk_nde_exp_t*)nde;
|
|
|
|
QSE_ASSERT (exp->type == QSE_AWK_NDE_EXP_INCPRE);
|
|
QSE_ASSERT (exp->left != QSE_NULL && exp->right == QSE_NULL);
|
|
|
|
/* this way of checking if the l-value is assignable is
|
|
* ugly as it is dependent on the node types defined in qse/awk/awk.h
|
|
* but let's keep going this way for the time being. */
|
|
if (exp->left->type < QSE_AWK_NDE_NAMED ||
|
|
/*exp->left->type > QSE_AWK_NDE_ARGIDX) XXX */
|
|
exp->left->type > QSE_AWK_NDE_POS)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EOPERAND, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (exp->opcode == QSE_AWK_INCOP_PLUS)
|
|
{
|
|
inc_val_int = 1;
|
|
inc_val_flt = 1.0;
|
|
}
|
|
else if (exp->opcode == QSE_AWK_INCOP_MINUS)
|
|
{
|
|
inc_val_int = -1;
|
|
inc_val_flt = -1.0;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (!"should never happen - invalid opcode");
|
|
SETERR_LOC (rtx, QSE_AWK_EINTERN, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_ASSERT (exp->left->next == QSE_NULL);
|
|
left = eval_expression (rtx, exp->left);
|
|
if (left == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, left);
|
|
left_vtype = QSE_AWK_RTX_GETVALTYPE (rtx, left);
|
|
|
|
switch (left_vtype)
|
|
{
|
|
case QSE_AWK_VAL_INT:
|
|
{
|
|
qse_awk_int_t r = QSE_AWK_RTX_GETINTFROMVAL (rtx, left);
|
|
res = qse_awk_rtx_makeintval (rtx, r + inc_val_int);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_VAL_FLT:
|
|
{
|
|
qse_awk_flt_t r = ((qse_awk_val_flt_t*)left)->val;
|
|
res = qse_awk_rtx_makefltval (rtx, r + inc_val_flt);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
qse_awk_int_t v1;
|
|
qse_awk_flt_t v2;
|
|
int n;
|
|
|
|
n = qse_awk_rtx_valtonum (rtx, left, &v1, &v2);
|
|
if (n <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
res = qse_awk_rtx_makeintval (rtx, v1 + inc_val_int);
|
|
}
|
|
else /* if (n == 1) */
|
|
{
|
|
QSE_ASSERT (n == 1);
|
|
res = qse_awk_rtx_makefltval (rtx, v2 + inc_val_flt);
|
|
}
|
|
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (do_assignment (rtx, exp->left, res) == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_incpst (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* left, * res, * res2;
|
|
qse_awk_val_type_t left_vtype;
|
|
qse_awk_int_t inc_val_int;
|
|
qse_awk_flt_t inc_val_flt;
|
|
qse_awk_nde_exp_t* exp = (qse_awk_nde_exp_t*)nde;
|
|
|
|
QSE_ASSERT (exp->type == QSE_AWK_NDE_EXP_INCPST);
|
|
QSE_ASSERT (exp->left != QSE_NULL && exp->right == QSE_NULL);
|
|
|
|
/* this way of checking if the l-value is assignable is
|
|
* ugly as it is dependent on the node types defined in qse/awk/awk.h.
|
|
* but let's keep going this way for the time being. */
|
|
if (exp->left->type < QSE_AWK_NDE_NAMED ||
|
|
/*exp->left->type > QSE_AWK_NDE_ARGIDX) XXX */
|
|
exp->left->type > QSE_AWK_NDE_POS)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EOPERAND, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (exp->opcode == QSE_AWK_INCOP_PLUS)
|
|
{
|
|
inc_val_int = 1;
|
|
inc_val_flt = 1.0;
|
|
}
|
|
else if (exp->opcode == QSE_AWK_INCOP_MINUS)
|
|
{
|
|
inc_val_int = -1;
|
|
inc_val_flt = -1.0;
|
|
}
|
|
else
|
|
{
|
|
QSE_ASSERT (!"should never happen - invalid opcode");
|
|
SETERR_LOC (rtx, QSE_AWK_EINTERN, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_ASSERT (exp->left->next == QSE_NULL);
|
|
left = eval_expression (rtx, exp->left);
|
|
if (left == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, left);
|
|
|
|
left_vtype = QSE_AWK_RTX_GETVALTYPE (rtx, left);
|
|
|
|
switch (left_vtype)
|
|
{
|
|
case QSE_AWK_VAL_INT:
|
|
{
|
|
qse_awk_int_t r = QSE_AWK_RTX_GETINTFROMVAL (rtx, left);
|
|
res = qse_awk_rtx_makeintval (rtx, r);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res2 = qse_awk_rtx_makeintval (rtx, r + inc_val_int);
|
|
if (res2 == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
qse_awk_rtx_freeval (rtx, res, 1);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case QSE_AWK_VAL_FLT:
|
|
{
|
|
qse_awk_flt_t r = ((qse_awk_val_flt_t*)left)->val;
|
|
res = qse_awk_rtx_makefltval (rtx, r);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res2 = qse_awk_rtx_makefltval (rtx, r + inc_val_flt);
|
|
if (res2 == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
qse_awk_rtx_freeval (rtx, res, 1);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
qse_awk_int_t v1;
|
|
qse_awk_flt_t v2;
|
|
int n;
|
|
|
|
n = qse_awk_rtx_valtonum (rtx, left, &v1, &v2);
|
|
if (n <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
res = qse_awk_rtx_makeintval (rtx, v1);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res2 = qse_awk_rtx_makeintval (rtx, v1 + inc_val_int);
|
|
if (res2 == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
qse_awk_rtx_freeval (rtx, res, 1);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
else /* if (n == 1) */
|
|
{
|
|
QSE_ASSERT (n == 1);
|
|
res = qse_awk_rtx_makefltval (rtx, v2);
|
|
if (res == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
res2 = qse_awk_rtx_makefltval (rtx, v2 + inc_val_flt);
|
|
if (res2 == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
qse_awk_rtx_freeval (rtx, res, 1);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (do_assignment (rtx, exp->left, res2) == QSE_NULL)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, left);
|
|
return res;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_cnd (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* tv, * v;
|
|
qse_awk_nde_cnd_t* cnd = (qse_awk_nde_cnd_t*)nde;
|
|
|
|
QSE_ASSERT (cnd->test->next == QSE_NULL);
|
|
|
|
tv = eval_expression (run, cnd->test);
|
|
if (tv == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, tv);
|
|
|
|
QSE_ASSERT (cnd->left->next == QSE_NULL && cnd->right->next == QSE_NULL);
|
|
v = (qse_awk_rtx_valtobool(run, tv))? eval_expression(run, cnd->left): eval_expression(run, cnd->right);
|
|
|
|
qse_awk_rtx_refdownval (run, tv);
|
|
return v;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_fncall_fnc (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
/* intrinsic function */
|
|
qse_awk_nde_fncall_t* call = (qse_awk_nde_fncall_t*)nde;
|
|
/* the parser must make sure that the number of arguments is proper */
|
|
QSE_ASSERT (call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
|
|
return __eval_call(rtx, nde, QSE_NULL, push_arg_from_nde, (void*)call->u.fnc.spec.arg.spec, QSE_NULL, QSE_NULL);
|
|
}
|
|
|
|
static QSE_INLINE qse_awk_val_t* eval_fncall_fun_ex (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, void(*errhandler)(void*), void* eharg)
|
|
{
|
|
qse_awk_nde_fncall_t* call = (qse_awk_nde_fncall_t*)nde;
|
|
qse_awk_fun_t* fun;
|
|
qse_htb_pair_t* pair;
|
|
|
|
if (!call->u.fun.fun)
|
|
{
|
|
/* there can be multiple runtime instances for a single awk 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 = qse_htb_search(rtx->awk->tree.funs, call->u.fun.name.ptr, call->u.fun.name.len);
|
|
if (!pair)
|
|
{
|
|
SETERR_ARGX_LOC (rtx, QSE_AWK_EFUNNF, &call->u.fun.name, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
fun = (qse_awk_fun_t*)QSE_HTB_VPTR(pair);
|
|
QSE_ASSERT (fun != QSE_NULL);
|
|
|
|
/* cache the search result */
|
|
call->u.fun.fun = fun;
|
|
}
|
|
else
|
|
{
|
|
/* use the cached function */
|
|
fun = call->u.fun.fun;
|
|
}
|
|
|
|
if (call->nargs > fun->nargs)
|
|
{
|
|
/* TODO: is this correct? what if i want to
|
|
* allow arbitarary numbers of arguments? */
|
|
SETERR_LOC (rtx, QSE_AWK_EARGTM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
return __eval_call(rtx, nde, fun, push_arg_from_nde, QSE_NULL, errhandler, eharg);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_fncall_fun (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return eval_fncall_fun_ex(rtx, nde, QSE_NULL, QSE_NULL);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_fncall_var (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_nde_fncall_t* call = (qse_awk_nde_fncall_t*)nde;
|
|
qse_awk_val_t* fv, * rv;
|
|
|
|
fv = eval_expression(rtx, (qse_awk_nde_t*)call->u.var.var);
|
|
if (!fv) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, fv);
|
|
if (QSE_AWK_RTX_GETVALTYPE(rtx, fv) != QSE_AWK_VAL_FUN)
|
|
{
|
|
SETERR_ARGX_LOC (rtx, QSE_AWK_ENOTFUN, &call->u.var.var->id.name, &nde->loc);
|
|
rv = QSE_NULL;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_fun_t* fun = ((qse_awk_val_fun_t*)fv)->fun;
|
|
rv = __eval_call(rtx, nde, fun, push_arg_from_nde, QSE_NULL, QSE_NULL, QSE_NULL);
|
|
}
|
|
qse_awk_rtx_refdownval (rtx, fv);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* run->stack_base has not been set for this
|
|
* stack frame. so the RTX_STACK_ARG macro cannot be used as in
|
|
* qse_awk_rtx_refdownval (run, RTX_STACK_ARG(run,nargs));*/
|
|
#define UNWIND_RTX_STACK_ARG(rtx,nargs) \
|
|
do { \
|
|
while ((nargs) > 0) \
|
|
{ \
|
|
--(nargs); \
|
|
qse_awk_rtx_refdownval ((rtx), (rtx)->stack[(rtx)->stack_top-1]); \
|
|
__raw_pop (rtx); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define UNWIND_RTX_STACK_BASE(rtx) \
|
|
do { \
|
|
__raw_pop (rtx); /* nargs */ \
|
|
__raw_pop (rtx); /* return */ \
|
|
__raw_pop (rtx); /* prev stack top */ \
|
|
__raw_pop (rtx); /* prev stack back */ \
|
|
} while (0)
|
|
|
|
#define UNWIND_RTX_STACK(rtx,nargs) \
|
|
do { \
|
|
UNWIND_RTX_STACK_ARG (rtx,nargs); \
|
|
UNWIND_RTX_STACK_BASE (rtx); \
|
|
} while (0)
|
|
|
|
static qse_awk_val_t* __eval_call (
|
|
qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, qse_awk_fun_t* fun,
|
|
qse_size_t(*argpusher)(qse_awk_rtx_t*,qse_awk_nde_fncall_t*,void*), void* apdata,
|
|
void(*errhandler)(void*), void* eharg)
|
|
{
|
|
qse_awk_nde_fncall_t* call = (qse_awk_nde_fncall_t*)nde;
|
|
qse_size_t saved_stack_top;
|
|
qse_size_t nargs, i;
|
|
qse_awk_val_t* v;
|
|
int n;
|
|
|
|
/*
|
|
* ---------------------
|
|
* 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
|
|
* ---------------------
|
|
*/
|
|
|
|
QSE_ASSERT (QSE_SIZEOF(void*) >= QSE_SIZEOF(rtx->stack_top));
|
|
QSE_ASSERT (QSE_SIZEOF(void*) >= QSE_SIZEOF(rtx->stack_base));
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("setting up function stack frame top=%zd base=%zd\n"), (qse_size_t)rtx->stack_top, (qse_size_t)rtx->stack_base);
|
|
#endif
|
|
|
|
if (__raw_push(rtx,(void*)rtx->stack_base) <= -1)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (__raw_push(rtx,(void*)saved_stack_top) <= -1)
|
|
{
|
|
__raw_pop (rtx);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* secure space for a return value. */
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1)
|
|
{
|
|
__raw_pop (rtx);
|
|
__raw_pop (rtx);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* secure space for nargs */
|
|
if (__raw_push(rtx,qse_awk_val_nil) <= -1)
|
|
{
|
|
__raw_pop (rtx);
|
|
__raw_pop (rtx);
|
|
__raw_pop (rtx);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
/* push all arguments onto the stack */
|
|
nargs = argpusher(rtx, call, apdata);
|
|
if (nargs == (qse_size_t)-1)
|
|
{
|
|
UNWIND_RTX_STACK_BASE (rtx);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_ASSERT (nargs == call->nargs);
|
|
|
|
if (fun)
|
|
{
|
|
/* extra step for normal awk functions */
|
|
|
|
if (fun->argspec && call->args) /* qse_awk_rtx_callfun() sets up a fake call structure with nargs > 0 but args == QSE_NULL */
|
|
{
|
|
/* sanity check for pass-by-reference parameters of a normal awk function.
|
|
* it tests if each pass-by-reference argument is referenceable. */
|
|
|
|
qse_awk_nde_t* p = call->args;
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
if (fun->argspec[i] == QSE_T('r'))
|
|
{
|
|
qse_awk_val_t** ref;
|
|
|
|
if (get_reference(rtx, p, &ref) <= -1)
|
|
{
|
|
UNWIND_RTX_STACK (rtx, nargs);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
p = p->next;
|
|
}
|
|
}
|
|
|
|
while (nargs < fun->nargs)
|
|
{
|
|
/* push as many nils as the number of missing actual arguments */
|
|
if (__raw_push(rtx, qse_awk_val_nil) <= -1)
|
|
{
|
|
UNWIND_RTX_STACK (rtx, nargs);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
nargs++;
|
|
}
|
|
}
|
|
|
|
rtx->stack_base = saved_stack_top;
|
|
RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("running function body\n"));
|
|
#endif
|
|
|
|
if (fun)
|
|
{
|
|
/* normal awk function */
|
|
QSE_ASSERT (fun->body->type == QSE_AWK_NDE_BLK);
|
|
n = run_block(rtx,(qse_awk_nde_blk_t*)fun->body);
|
|
}
|
|
else
|
|
{
|
|
n = 0;
|
|
|
|
/* intrinsic function */
|
|
QSE_ASSERT (call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
|
|
|
|
if (call->u.fnc.spec.impl)
|
|
{
|
|
rtx->errinf.num = QSE_AWK_ENOERR;
|
|
|
|
n = call->u.fnc.spec.impl(rtx, &call->u.fnc.info);
|
|
|
|
if (n <= -1)
|
|
{
|
|
if (rtx->errinf.num == QSE_AWK_ENOERR)
|
|
{
|
|
/* the handler has not set the error.
|
|
* fix it */
|
|
SETERR_ARGX_LOC (
|
|
rtx, QSE_AWK_EFNCIMPL,
|
|
&call->u.fnc.info.name, &nde->loc
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
}
|
|
|
|
/* correct the return code just in case */
|
|
if (n < -1) n = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* refdown args in the rtx.stack */
|
|
nargs = (qse_size_t)RTX_STACK_NARGS(rtx);
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("block rtx complete nargs = %d\n"), (int)nargs);
|
|
#endif
|
|
|
|
if (fun && fun->argspec && call->args && call->nargs > 0) /* qse_awk_rtx_callfun() sets up a fake call structure with nargs > 0 but args == QSE_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 qse_awk_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.
|
|
*/
|
|
|
|
qse_size_t cur_stack_base = rtx->stack_base;
|
|
qse_size_t prev_stack_base = (qse_size_t)rtx->stack[rtx->stack_base + 0];
|
|
|
|
qse_awk_nde_t* p = call->args;
|
|
for (i = 0; i < call->nargs; i++)
|
|
{
|
|
if (fun->argspec[i] == QSE_T('r'))
|
|
{
|
|
qse_awk_val_t** ref;
|
|
qse_awk_val_ref_t refv;
|
|
|
|
/* 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 assumptions for this is dependent on get_reference()
|
|
* implementation */
|
|
rtx->stack_base = prev_stack_base; /* UGLY */
|
|
get_reference (rtx, p, &ref); /* no failure check as it must succeed here for the check done above */
|
|
rtx->stack_base = cur_stack_base; /* UGLY */
|
|
|
|
QSE_AWK_RTX_INIT_REF_VAL (&refv, p->type - QSE_AWK_NDE_NAMED, ref, 9); /* initialize a fake reference variable. 9 chosen randomly */
|
|
qse_awk_rtx_setrefval (rtx, &refv, RTX_STACK_ARG(rtx, i));
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
|
|
p = p->next;
|
|
}
|
|
|
|
for (; i < nargs; i++)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("got return value\n"));
|
|
#endif
|
|
|
|
v = RTX_STACK_RETVAL(rtx);
|
|
if (n == -1)
|
|
{
|
|
if (rtx->errinf.num == QSE_AWK_ENOERR && errhandler != QSE_NULL)
|
|
{
|
|
/* errhandler is passed only when __eval_call() is
|
|
* invoked from qse_awk_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 RTX_STACK_RETVAL(rtx)
|
|
* effectively becomes RTX_STACK_RETVAL_GBL(rtx).
|
|
* As __eval_call() returns QSE_NULL on error and
|
|
* the reference count of 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 QSE_AWK_ENOERR and gets RTX_STACK_RETVAL_GBL(rtx)
|
|
* to determine if it is terminated by exit().
|
|
*
|
|
* The handler capture_retval_on_exit()
|
|
* increments the reference of 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 */
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
RTX_STACK_RETVAL(rtx) = qse_awk_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 qse_awk_rtx_setretval()
|
|
* regardless of its reference count. */
|
|
qse_awk_rtx_refdownval_nofree (rtx, v);
|
|
}
|
|
|
|
rtx->stack_top = (qse_size_t)rtx->stack[rtx->stack_base + 1];
|
|
rtx->stack_base = (qse_size_t)rtx->stack[rtx->stack_base + 0];
|
|
|
|
if (rtx->exit_level == EXIT_FUNCTION) rtx->exit_level = EXIT_NONE;
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("returning from function top=%zd, base=%zd\n"), (qse_size_t)rtx->stack_top, (qse_size_t)rtx->stack_base);
|
|
#endif
|
|
return (n == -1)? QSE_NULL: v;
|
|
}
|
|
|
|
static qse_size_t push_arg_from_vals (qse_awk_rtx_t* rtx, qse_awk_nde_fncall_t* call, void* data)
|
|
{
|
|
struct pafv_t* pafv = (struct pafv_t*)data;
|
|
qse_size_t nargs = 0;
|
|
|
|
for (nargs = 0; nargs < pafv->nargs; nargs++)
|
|
{
|
|
if (pafv->argspec && pafv->argspec[nargs] == QSE_T('r'))
|
|
{
|
|
qse_awk_val_t** ref;
|
|
qse_awk_val_t* v;
|
|
|
|
ref = (qse_awk_val_t**)&pafv->args[nargs];
|
|
v = qse_awk_rtx_makerefval(rtx, QSE_AWK_VAL_REF_LCL, ref); /* this type(QSE_AWK_VAL_REF_LCL) is fake */
|
|
if (!v)
|
|
{
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &call->loc);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
if (__raw_push(rtx, v) <= -1)
|
|
{
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &call->loc);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
}
|
|
else
|
|
{
|
|
if (__raw_push(rtx, pafv->args[nargs]) <= -1)
|
|
{
|
|
/* ugly - arg needs to be freed if it doesn't have
|
|
* any reference. but its reference has not been
|
|
* updated yet as it is carried out after successful
|
|
* stack push. so it adds up a reference and
|
|
* dereferences it */
|
|
qse_awk_rtx_refupval (rtx, pafv->args[nargs]);
|
|
qse_awk_rtx_refdownval (rtx, pafv->args[nargs]);
|
|
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &call->loc);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, pafv->args[nargs]);
|
|
}
|
|
}
|
|
|
|
return nargs;
|
|
}
|
|
|
|
static qse_size_t push_arg_from_nde (qse_awk_rtx_t* rtx, qse_awk_nde_fncall_t* call, void* data)
|
|
{
|
|
qse_awk_nde_t* p;
|
|
qse_awk_val_t* v;
|
|
qse_size_t nargs;
|
|
const qse_char_t* fnc_arg_spec = (const qse_char_t*)data;
|
|
|
|
for (p = call->args, nargs = 0; p != QSE_NULL; p = p->next, nargs++)
|
|
{
|
|
/* if fnc_arg_spec is to be provided, it must contain as many characters as nargs */
|
|
|
|
if (fnc_arg_spec && fnc_arg_spec[nargs] == QSE_T('r'))
|
|
{
|
|
qse_awk_val_t** ref;
|
|
|
|
if (get_reference(rtx, p, &ref) <= -1)
|
|
{
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
/* 'p->type - QSE_AWK_NDE_NAMED' must produce a relevant QSE_AWK_VAL_REF_XXX value. */
|
|
v = qse_awk_rtx_makerefval(rtx, p->type - QSE_AWK_NDE_NAMED, ref);
|
|
}
|
|
else if (fnc_arg_spec && fnc_arg_spec[nargs] == QSE_T('x'))
|
|
{
|
|
/* a regular expression is passed to
|
|
* the function as it is */
|
|
v = eval_expression0(rtx, p);
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression(rtx, p);
|
|
}
|
|
|
|
if (!v)
|
|
{
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
if (__raw_push(rtx,v) <= -1)
|
|
{
|
|
/* ugly - v needs to be freed if it doesn't have
|
|
* any reference. but its reference has not been
|
|
* updated yet as it is carried out after the
|
|
* successful stack push. so it adds up a reference
|
|
* and dereferences it */
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
UNWIND_RTX_STACK_ARG (rtx, nargs);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &call->loc);
|
|
return (qse_size_t)-1;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
}
|
|
|
|
QSE_ASSERT (call->nargs == nargs);
|
|
return nargs;
|
|
}
|
|
|
|
static int get_reference (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, qse_awk_val_t*** ref)
|
|
{
|
|
qse_awk_nde_var_t* tgt = (qse_awk_nde_var_t*)nde;
|
|
qse_awk_val_t** tmp;
|
|
|
|
/* refer to eval_indexed for application of a similar concept */
|
|
|
|
switch (nde->type)
|
|
{
|
|
case QSE_AWK_NDE_NAMED:
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
pair = qse_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
/* it is bad that the named variable has to be created here.
|
|
* would there be any better ways to avoid this? */
|
|
pair = qse_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, qse_awk_val_nil, 0);
|
|
if (!pair)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
*ref = (qse_awk_val_t**)&QSE_HTB_VPTR(pair);
|
|
return 0;
|
|
}
|
|
|
|
case QSE_AWK_NDE_GBL:
|
|
/* *ref = (qse_awk_val_t**)&RTX_STACK_GBL(rtx,tgt->id.idxa); */
|
|
*ref = (qse_awk_val_t**)((qse_size_t)tgt->id.idxa);
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_LCL:
|
|
*ref = (qse_awk_val_t**)&RTX_STACK_LCL(rtx,tgt->id.idxa);
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_ARG:
|
|
*ref = (qse_awk_val_t**)&RTX_STACK_ARG(rtx,tgt->id.idxa);
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_NAMEDIDX:
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
pair = qse_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
pair = qse_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, qse_awk_val_nil, 0);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
tmp = get_reference_indexed(rtx, tgt, (qse_awk_val_t**)&QSE_HTB_VPTR(pair));
|
|
if (tmp == QSE_NULL) return -1;
|
|
*ref = tmp;
|
|
return 0;
|
|
}
|
|
|
|
case QSE_AWK_NDE_GBLIDX:
|
|
tmp = get_reference_indexed(rtx, tgt, (qse_awk_val_t**)&RTX_STACK_GBL(rtx,tgt->id.idxa));
|
|
if (tmp == QSE_NULL) return -1;
|
|
*ref = tmp;
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_LCLIDX:
|
|
tmp = get_reference_indexed(rtx, tgt, (qse_awk_val_t**)&RTX_STACK_LCL(rtx,tgt->id.idxa));
|
|
if (tmp == QSE_NULL) return -1;
|
|
*ref = tmp;
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_ARGIDX:
|
|
tmp = get_reference_indexed(rtx, tgt, (qse_awk_val_t**)&RTX_STACK_ARG(rtx,tgt->id.idxa));
|
|
if (tmp == QSE_NULL) return -1;
|
|
*ref = tmp;
|
|
return 0;
|
|
|
|
case QSE_AWK_NDE_POS:
|
|
{
|
|
int n;
|
|
qse_awk_int_t lv;
|
|
qse_awk_val_t* v;
|
|
|
|
/* the position number is returned for the positional
|
|
* variable unlike other reference types. */
|
|
v = eval_expression(rtx, ((qse_awk_nde_pos_t*)nde)->val);
|
|
if (v == QSE_NULL) return -1;
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint(rtx, v, &lv);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
if (n <= -1)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EPOSIDX, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
if (!IS_VALID_POSIDX(lv))
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_EPOSIDX, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
*ref = (qse_awk_val_t**)((qse_size_t)lv);
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
SETERR_LOC (rtx, QSE_AWK_ENOTREF, &nde->loc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static qse_awk_val_t** get_reference_indexed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* nde, qse_awk_val_t** val)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
qse_char_t* str;
|
|
qse_size_t len;
|
|
qse_char_t idxbuf[IDXBUFSIZE];
|
|
qse_awk_val_type_t vtype;
|
|
|
|
QSE_ASSERT (val != QSE_NULL);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE(rtx, *val);
|
|
if (vtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
|
|
tmp = qse_awk_rtx_makemapval (rtx);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, *val);
|
|
*val = tmp;
|
|
qse_awk_rtx_refupval (rtx, (qse_awk_val_t*)*val);
|
|
}
|
|
else if (vtype != QSE_AWK_VAL_MAP)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOTMAP, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_ASSERT (nde->idx != QSE_NULL);
|
|
|
|
len = QSE_COUNTOF(idxbuf);
|
|
str = idxnde_to_str (rtx, nde->idx, idxbuf, &len);
|
|
if (str == QSE_NULL) return QSE_NULL;
|
|
|
|
pair = qse_htb_search((*(qse_awk_val_map_t**)val)->map, str, len);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
pair = qse_htb_upsert((*(qse_awk_val_map_t**)val)->map, str, len, qse_awk_val_nil, 0);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, QSE_HTB_VPTR(pair));
|
|
}
|
|
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
return (qse_awk_val_t**)&QSE_HTB_VPTR(pair);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_int (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
val = qse_awk_rtx_makeintval(rtx, ((qse_awk_nde_int_t*)nde)->val);
|
|
if (val == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
else if (QSE_AWK_VTR_IS_POINTER(val)) ((qse_awk_val_int_t*)val)->nde = nde;
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_flt (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
val = qse_awk_rtx_makefltval(rtx, ((qse_awk_nde_flt_t*)nde)->val);
|
|
if (val == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
else ((qse_awk_val_flt_t*)val)->nde = nde;
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_str (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
val = qse_awk_rtx_makestrval(rtx, ((qse_awk_nde_str_t*)nde)->ptr, ((qse_awk_nde_str_t*)nde)->len);
|
|
if (val == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_mbs (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
val = qse_awk_rtx_makembsval(rtx, ((qse_awk_nde_mbs_t*)nde)->ptr, ((qse_awk_nde_mbs_t*)nde)->len);
|
|
if (val == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_rex (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
val = qse_awk_rtx_makerexval(rtx, &((qse_awk_nde_rex_t*)nde)->str, ((qse_awk_nde_rex_t*)nde)->code);
|
|
if (val == QSE_NULL) ADJERR_LOC (rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_fun (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_val_t* val;
|
|
qse_awk_fun_t* fun;
|
|
|
|
|
|
fun = ((qse_awk_nde_fun_t*)nde)->funptr;
|
|
if (!fun)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
|
|
/* TODO: support bultin functions?, support module functions? */
|
|
pair = qse_htb_search(rtx->awk->tree.funs, ((qse_awk_nde_fun_t*)nde)->name.ptr, ((qse_awk_nde_fun_t*)nde)->name.len);
|
|
if (!pair)
|
|
{
|
|
SETERR_ARGX_LOC (rtx, QSE_AWK_EFUNNF, &((qse_awk_nde_fun_t*)nde)->name, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
fun = (qse_awk_fun_t*)QSE_HTB_VPTR(pair);
|
|
QSE_ASSERT (fun != QSE_NULL);
|
|
}
|
|
|
|
val = qse_awk_rtx_makefunval(rtx, fun);
|
|
if (!val) ADJERR_LOC (rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_named (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
pair = qse_htb_search (rtx->named, ((qse_awk_nde_var_t*)nde)->id.name.ptr, ((qse_awk_nde_var_t*)nde)->id.name.len);
|
|
return (pair == QSE_NULL)? qse_awk_val_nil: QSE_HTB_VPTR(pair);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_gbl (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return RTX_STACK_GBL(rtx,((qse_awk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_lcl (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return RTX_STACK_LCL(rtx,((qse_awk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_arg (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return RTX_STACK_ARG(rtx,((qse_awk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_indexed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* nde, qse_awk_val_t** val)
|
|
{
|
|
qse_htb_pair_t* pair;
|
|
qse_char_t* str;
|
|
qse_size_t len;
|
|
qse_char_t idxbuf[IDXBUFSIZE];
|
|
qse_awk_val_type_t vtype;
|
|
|
|
QSE_ASSERT (val != QSE_NULL);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE(rtx, *val);
|
|
if (vtype == QSE_AWK_VAL_NIL)
|
|
{
|
|
qse_awk_val_t* tmp;
|
|
|
|
tmp = qse_awk_rtx_makemapval(rtx);
|
|
if (tmp == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, *val);
|
|
*val = tmp;
|
|
qse_awk_rtx_refupval (rtx, (qse_awk_val_t*)*val);
|
|
}
|
|
else if (vtype != QSE_AWK_VAL_MAP)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOTMAP, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
QSE_ASSERT (nde->idx != QSE_NULL);
|
|
|
|
len = QSE_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, nde->idx, idxbuf, &len);
|
|
if (str == QSE_NULL) return QSE_NULL;
|
|
|
|
pair = qse_htb_search((*(qse_awk_val_map_t**)val)->map, str, len);
|
|
if (str != idxbuf) qse_awk_rtx_freemem (rtx, str);
|
|
|
|
return (pair == QSE_NULL)? qse_awk_val_nil: (qse_awk_val_t*)QSE_HTB_VPTR(pair);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_namedidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_nde_var_t* tgt = (qse_awk_nde_var_t*)nde;
|
|
qse_htb_pair_t* pair;
|
|
|
|
pair = qse_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
pair = qse_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, qse_awk_val_nil, 0);
|
|
if (pair == QSE_NULL)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, QSE_HTB_VPTR(pair));
|
|
}
|
|
|
|
return eval_indexed (rtx, tgt, (qse_awk_val_t**)&QSE_HTB_VPTR(pair));
|
|
}
|
|
|
|
static qse_awk_val_t* eval_gblidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (qse_awk_nde_var_t*)nde, (qse_awk_val_t**)&RTX_STACK_GBL(rtx,((qse_awk_nde_var_t*)nde)->id.idxa));
|
|
}
|
|
|
|
static qse_awk_val_t* eval_lclidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (qse_awk_nde_var_t*)nde, (qse_awk_val_t**)&RTX_STACK_LCL(rtx,((qse_awk_nde_var_t*)nde)->id.idxa));
|
|
}
|
|
|
|
static qse_awk_val_t* eval_argidx (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (qse_awk_nde_var_t*)nde, (qse_awk_val_t**)&RTX_STACK_ARG(rtx,((qse_awk_nde_var_t*)nde)->id.idxa));
|
|
}
|
|
|
|
static qse_awk_val_t* eval_pos (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_nde_pos_t* pos = (qse_awk_nde_pos_t*)nde;
|
|
qse_awk_val_t* v;
|
|
qse_awk_int_t lv;
|
|
int n;
|
|
|
|
v = eval_expression (run, pos->val);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (run, v);
|
|
n = qse_awk_rtx_valtoint (run, v, &lv);
|
|
qse_awk_rtx_refdownval (run, v);
|
|
if (n <= -1)
|
|
{
|
|
SETERR_LOC (run, QSE_AWK_EPOSIDX, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (lv < 0)
|
|
{
|
|
SETERR_LOC (run, QSE_AWK_EPOSIDX, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
if (lv == 0) v = run->inrec.d0;
|
|
else if (lv > 0 && lv <= (qse_awk_int_t)run->inrec.nflds)
|
|
v = run->inrec.flds[lv-1].val;
|
|
else v = qse_awk_val_zls; /*qse_awk_val_nil;*/
|
|
|
|
return v;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_getline (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde)
|
|
{
|
|
qse_awk_nde_getline_t* p;
|
|
qse_awk_val_t* v, * tmp;
|
|
qse_char_t* dst;
|
|
qse_str_t* buf;
|
|
int n, x;
|
|
qse_awk_val_type_t vtype;
|
|
|
|
p = (qse_awk_nde_getline_t*)nde;
|
|
|
|
QSE_ASSERT (
|
|
(p->in_type == QSE_AWK_IN_PIPE && p->in != QSE_NULL) ||
|
|
(p->in_type == QSE_AWK_IN_RWPIPE && p->in != QSE_NULL) ||
|
|
(p->in_type == QSE_AWK_IN_FILE && p->in != QSE_NULL) ||
|
|
(p->in_type == QSE_AWK_IN_CONSOLE && p->in == QSE_NULL));
|
|
|
|
if (p->in)
|
|
{
|
|
qse_size_t len;
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
v = eval_expression(rtx, p->in);
|
|
if (!v) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
dst = qse_awk_rtx_getvalstr(rtx, v, &len);
|
|
if (!dst)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (len <= 0)
|
|
{
|
|
|
|
qse_awk_rtx_freevalstr (rtx, v, dst);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
|
|
n = -1;
|
|
goto skip_read;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
if (dst[--len] == QSE_T('\0'))
|
|
{
|
|
/* the input source name contains a null
|
|
* character. make getline return -1.
|
|
* unlike print & printf, it is not a hard
|
|
* error */
|
|
qse_awk_rtx_freevalstr (rtx, v, dst);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
n = -1;
|
|
goto skip_read;
|
|
}
|
|
}
|
|
}
|
|
else dst = (qse_char_t*)QSE_T("");
|
|
|
|
buf = &rtx->inrec.lineg;
|
|
read_console_again:
|
|
qse_str_clear (&rtx->inrec.lineg);
|
|
|
|
n = qse_awk_rtx_readio(rtx, p->in_type, dst, buf);
|
|
|
|
if (p->in)
|
|
{
|
|
qse_awk_rtx_freevalstr (rtx, v, dst);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
}
|
|
|
|
if (n <= -1)
|
|
{
|
|
/* make getline return -1 */
|
|
n = -1;
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
if (p->in_type == QSE_AWK_IN_CONSOLE)
|
|
{
|
|
QSE_ASSERT (p->in == QSE_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) return QSE_NULL;
|
|
/* this jump is a bit dirty. the 'if' block below qse_awk_rtx_readio()
|
|
* will never be true. but this makes code confusing */
|
|
goto read_console_again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->var == QSE_NULL)
|
|
{
|
|
/* set $0 with the input value */
|
|
x = qse_awk_rtx_setrec(rtx, 0, QSE_STR_XSTR(buf));
|
|
if (x <= -1) return QSE_NULL;
|
|
}
|
|
else
|
|
{
|
|
qse_awk_val_t* v;
|
|
|
|
v = qse_awk_rtx_makestrvalwithcstr(rtx, QSE_STR_XSTR(buf));
|
|
if (v == QSE_NULL)
|
|
{
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
tmp = do_assignment(rtx, p->var, v);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (tmp == QSE_NULL) return QSE_NULL;
|
|
}
|
|
|
|
/* update FNR & NR if reading from console */
|
|
if (p->in_type == QSE_AWK_IN_CONSOLE &&
|
|
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1)
|
|
{
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
skip_read:
|
|
tmp = qse_awk_rtx_makeintval (rtx, n);
|
|
if (!tmp) ADJERR_LOC (rtx, &nde->loc);
|
|
return tmp;
|
|
}
|
|
|
|
static qse_awk_val_t* eval_print (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
|
|
{
|
|
int n = run_print(run, (qse_awk_nde_print_t*)nde);
|
|
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
|
|
else if (n <= -1) return QSE_NULL;
|
|
return qse_awk_rtx_makeintval(run, n);
|
|
}
|
|
|
|
static qse_awk_val_t* eval_printf (qse_awk_rtx_t* run, qse_awk_nde_t* nde)
|
|
{
|
|
int n = run_printf(run, (qse_awk_nde_print_t*)nde);
|
|
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
|
|
else if (n <= -1) return QSE_NULL;
|
|
return qse_awk_rtx_makeintval(run, n);
|
|
}
|
|
|
|
static int __raw_push (qse_awk_rtx_t* rtx, void* val)
|
|
{
|
|
if (rtx->stack_top >= rtx->stack_limit)
|
|
{
|
|
/*
|
|
void** tmp;
|
|
qse_size_t n;
|
|
|
|
n = rtx->stack_limit + RTX_STACK_INCREMENT;
|
|
|
|
tmp = (void**)qse_awk_rtx_reallocmem(rtx, rtx->stack, n * QSE_SIZEOF(void*));
|
|
if (!tmp) return -1;
|
|
|
|
rtx->stack = tmp;
|
|
rtx->stack_limit = n;
|
|
*/
|
|
qse_awk_rtx_seterrfmt (rtx, QSE_AWK_ESTACK, QSE_NULL, QSE_T("runtime stack full"));
|
|
return -1;
|
|
}
|
|
|
|
rtx->stack[rtx->stack_top++] = val;
|
|
return 0;
|
|
}
|
|
|
|
static int read_record (qse_awk_rtx_t* rtx)
|
|
{
|
|
qse_ssize_t n;
|
|
qse_str_t* buf;
|
|
|
|
read_again:
|
|
if (qse_awk_rtx_clrrec (rtx, 0) == -1) return -1;
|
|
|
|
buf = &rtx->inrec.line;
|
|
n = qse_awk_rtx_readio (rtx, QSE_AWK_IN_CONSOLE, QSE_T(""), buf);
|
|
if (n <= -1)
|
|
{
|
|
qse_awk_rtx_clrrec (rtx, 0);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef DEBUG_RUN
|
|
qse_errputstrf (QSE_T("record len = %d str=[%.*s]\n"), (int)QSE_STR_LEN(buf), (int)QSE_STR_LEN(buf), QSE_STR_PTR(buf));
|
|
#endif
|
|
if (n == 0)
|
|
{
|
|
QSE_ASSERT (QSE_STR_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 (qse_awk_rtx_setrec(rtx, 0, QSE_STR_XSTR(buf)) <= -1 ||
|
|
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static qse_char_t* idxnde_to_str (qse_awk_rtx_t* rtx, qse_awk_nde_t* nde, qse_char_t* buf, qse_size_t* len)
|
|
{
|
|
qse_char_t* str;
|
|
qse_awk_val_t* idx;
|
|
|
|
QSE_ASSERT (nde != QSE_NULL);
|
|
|
|
if (!nde->next)
|
|
{
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
|
|
/* single node index */
|
|
idx = eval_expression (rtx, nde);
|
|
if (!idx) return QSE_NULL;
|
|
|
|
qse_awk_rtx_refupval (rtx, idx);
|
|
|
|
str = QSE_NULL;
|
|
|
|
if (buf)
|
|
{
|
|
/* try with a fixed-size buffer if given */
|
|
out.type = QSE_AWK_RTX_VALTOSTR_CPLCPY;
|
|
out.u.cplcpy.ptr = buf;
|
|
out.u.cplcpy.len = *len;
|
|
|
|
if (qse_awk_rtx_valtostr (rtx, idx, &out) >= 0)
|
|
{
|
|
str = out.u.cplcpy.ptr;
|
|
*len = out.u.cplcpy.len;
|
|
QSE_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 = QSE_AWK_RTX_VALTOSTR_CPLDUP;
|
|
if (qse_awk_rtx_valtostr(rtx, idx, &out) <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, idx);
|
|
ADJERR_LOC (rtx, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str = out.u.cpldup.ptr;
|
|
*len = out.u.cpldup.len;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, idx);
|
|
}
|
|
else
|
|
{
|
|
/* multidimensional index */
|
|
qse_str_t idxstr;
|
|
qse_cstr_t tmp;
|
|
qse_awk_rtx_valtostr_out_t out;
|
|
qse_awk_nde_t* xnde;
|
|
|
|
out.type = QSE_AWK_RTX_VALTOSTR_STRPCAT;
|
|
out.u.strpcat = &idxstr;
|
|
|
|
if (qse_str_init(&idxstr, qse_awk_rtx_getmmgr(rtx), DEF_BUF_CAPA) <= -1)
|
|
{
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
xnde = nde;
|
|
while (nde)
|
|
{
|
|
idx = eval_expression(rtx, nde);
|
|
if (!idx)
|
|
{
|
|
qse_str_fini (&idxstr);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, idx);
|
|
|
|
if (nde != xnde && qse_str_ncat(&idxstr, rtx->gbl.subsep.ptr, rtx->gbl.subsep.len) == (qse_size_t)-1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, idx);
|
|
qse_str_fini (&idxstr);
|
|
SETERR_LOC (rtx, QSE_AWK_ENOMEM, &nde->loc);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (qse_awk_rtx_valtostr(rtx, idx, &out) <= -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, idx);
|
|
qse_str_fini (&idxstr);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, idx);
|
|
nde = nde->next;
|
|
}
|
|
|
|
qse_str_yield (&idxstr, &tmp, 0);
|
|
str = tmp.ptr;
|
|
*len = tmp.len;
|
|
|
|
qse_str_fini (&idxstr);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
qse_char_t* qse_awk_rtx_format (
|
|
qse_awk_rtx_t* rtx, qse_str_t* out, qse_str_t* fbu,
|
|
const qse_char_t* fmt, qse_size_t fmt_len,
|
|
qse_size_t nargs_on_stack, qse_awk_nde_t* args, qse_size_t* len)
|
|
{
|
|
qse_size_t i;
|
|
qse_size_t stack_arg_idx = 1;
|
|
qse_awk_val_t* val;
|
|
|
|
#define OUT_CHAR(c) QSE_BLOCK( \
|
|
if (qse_str_ccat(out, (c)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define OUT_STR(ptr,len) QSE_BLOCK( \
|
|
if (qse_str_ncat(out, (ptr), (len)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define FMT_CHAR(c) QSE_BLOCK( \
|
|
if (qse_str_ccat(fbu, (c)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define FMT_STR(ptr,len) QSE_BLOCK( \
|
|
if (qse_str_ncat(fbu, (ptr), (len)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define GROW(buf) QSE_BLOCK( \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
qse_awk_rtx_freemem (rtx, (buf)->ptr); \
|
|
(buf)->ptr = QSE_NULL; \
|
|
} \
|
|
(buf)->len += (buf)->inc; \
|
|
(buf)->ptr = (qse_char_t*)qse_awk_rtx_allocmem(rtx, (buf)->len * QSE_SIZEOF(qse_char_t)); \
|
|
if ((buf)->ptr == QSE_NULL) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
(buf)->len = 0; \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define GROW_WITH_INC(buf,incv) QSE_BLOCK( \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
qse_awk_rtx_freemem (rtx, (buf)->ptr); \
|
|
(buf)->ptr = QSE_NULL; \
|
|
} \
|
|
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
|
|
(buf)->ptr = (qse_char_t*)qse_awk_rtx_allocmem(rtx, (buf)->len * QSE_SIZEOF(qse_char_t)); \
|
|
if ((buf)->ptr == QSE_NULL) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
(buf)->len = 0; \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
QSE_ASSERTX (rtx->format.tmp.ptr != QSE_NULL,
|
|
"run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called");
|
|
|
|
if (nargs_on_stack == (qse_size_t)-1)
|
|
{
|
|
val = (qse_awk_val_t*)args;
|
|
nargs_on_stack = 2;
|
|
}
|
|
else
|
|
{
|
|
val = QSE_NULL;
|
|
}
|
|
|
|
if (out == QSE_NULL) out = &rtx->format.out;
|
|
if (fbu == QSE_NULL) fbu = &rtx->format.fmt;
|
|
|
|
qse_str_clear (out);
|
|
qse_str_clear (fbu);
|
|
|
|
for (i = 0; i < fmt_len; i++)
|
|
{
|
|
qse_awk_int_t wp[2];
|
|
int wp_idx;
|
|
#define WP_WIDTH 0
|
|
#define WP_PRECISION 1
|
|
|
|
int flags;
|
|
#define FLAG_SPACE (1 << 0)
|
|
#define FLAG_HASH (1 << 1)
|
|
#define FLAG_ZERO (1 << 2)
|
|
#define FLAG_PLUS (1 << 3)
|
|
#define FLAG_MINUS (1 << 4)
|
|
|
|
if (QSE_STR_LEN(fbu) == 0)
|
|
{
|
|
/* format specifier is empty */
|
|
if (fmt[i] == QSE_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 QSE_T(' '):
|
|
flags |= FLAG_SPACE;
|
|
break;
|
|
case QSE_T('#'):
|
|
flags |= FLAG_HASH;
|
|
break;
|
|
case QSE_T('0'):
|
|
flags |= FLAG_ZERO;
|
|
break;
|
|
case QSE_T('+'):
|
|
flags |= FLAG_PLUS;
|
|
break;
|
|
case QSE_T('-'):
|
|
flags |= FLAG_MINUS;
|
|
break;
|
|
default:
|
|
goto wp_mod_init;
|
|
}
|
|
|
|
FMT_CHAR (fmt[i]); i++;
|
|
}
|
|
|
|
wp_mod_init:
|
|
wp[WP_WIDTH] = -1; /* width */
|
|
wp[WP_PRECISION] = -1; /* precision */
|
|
wp_idx = WP_WIDTH; /* width first */
|
|
|
|
wp_mod_main:
|
|
if (i < fmt_len && fmt[i] == QSE_T('*'))
|
|
{
|
|
/* variable width/precision modifier.
|
|
* take the width/precision from a parameter and
|
|
* transform it to a fixed length format */
|
|
qse_awk_val_t* v;
|
|
int n;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint(rtx, v, &wp[wp_idx]);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
do
|
|
{
|
|
n = qse_fmtintmax (
|
|
rtx->format.tmp.ptr,
|
|
rtx->format.tmp.len,
|
|
wp[wp_idx],
|
|
10 | QSE_FMTINTMAX_NOTRUNC | QSE_FMTINTMAX_NONULL,
|
|
-1,
|
|
QSE_T('\0'),
|
|
QSE_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 == QSE_NULL || val != QSE_NULL) stack_arg_idx++;
|
|
else args = args->next;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* fixed width/precision modifier */
|
|
if (i < fmt_len && QSE_AWK_ISDIGIT(rtx->awk, fmt[i]))
|
|
{
|
|
wp[wp_idx] = 0;
|
|
do
|
|
{
|
|
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - QSE_T('0');
|
|
FMT_CHAR (fmt[i]); i++;
|
|
}
|
|
while (i < fmt_len && QSE_AWK_ISDIGIT(rtx->awk, fmt[i]));
|
|
}
|
|
}
|
|
|
|
if (wp_idx == WP_WIDTH && i < fmt_len && fmt[i] == QSE_T('.'))
|
|
{
|
|
wp[WP_PRECISION] = 0;
|
|
FMT_CHAR (fmt[i]); i++;
|
|
|
|
wp_idx = WP_PRECISION; /* change index to precision */
|
|
goto wp_mod_main;
|
|
}
|
|
|
|
if (i >= fmt_len) break;
|
|
|
|
if (fmt[i] == QSE_T('d') || fmt[i] == QSE_T('i') ||
|
|
fmt[i] == QSE_T('x') || fmt[i] == QSE_T('X') ||
|
|
fmt[i] == QSE_T('b') || fmt[i] == QSE_T('B') ||
|
|
fmt[i] == QSE_T('o') || fmt[i] == QSE_T('u'))
|
|
{
|
|
qse_awk_val_t* v;
|
|
qse_awk_int_t l;
|
|
int n;
|
|
|
|
int fmt_flags;
|
|
int fmt_uint = 0;
|
|
int fmt_width;
|
|
qse_char_t fmt_fill = QSE_T('\0');
|
|
const qse_char_t* fmt_prefix = QSE_NULL;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint (rtx, v, &l);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
fmt_flags = QSE_FMTINTMAX_NOTRUNC | QSE_FMTINTMAX_NONULL;
|
|
|
|
if (l == 0 && wp[WP_PRECISION] == 0)
|
|
{
|
|
/* A zero value with a precision of zero produces
|
|
* no character. */
|
|
fmt_flags |= QSE_FMTINTMAX_NOZERO;
|
|
}
|
|
|
|
if (wp[WP_WIDTH] != -1)
|
|
{
|
|
QSE_ASSERTX (wp[WP_WIDTH] > 0, "Width must be greater than 0 if specified");
|
|
|
|
/* justification when width is specified. */
|
|
if (flags & FLAG_ZERO)
|
|
{
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* FLAG_MINUS wins if both FLAG_ZERO
|
|
* and FLAG_MINUS are specified. */
|
|
fmt_fill = QSE_T(' ');
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= QSE_FMTINTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wp[WP_PRECISION] == -1)
|
|
{
|
|
/* precision not specified.
|
|
* FLAG_ZERO can take effect */
|
|
fmt_fill = QSE_T('0');
|
|
fmt_flags |= QSE_FMTINTMAX_FILLCENTER;
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = QSE_T(' ');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = QSE_T(' ');
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= QSE_FMTINTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (fmt[i])
|
|
{
|
|
case QSE_T('B'):
|
|
case QSE_T('b'):
|
|
fmt_flags |= 2;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0b */
|
|
fmt_prefix = QSE_T("0b");
|
|
}
|
|
break;
|
|
|
|
case QSE_T('X'):
|
|
fmt_flags |= QSE_FMTINTMAX_UPPERCASE;
|
|
case QSE_T('x'):
|
|
fmt_flags |= 16;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0x */
|
|
fmt_prefix = QSE_T("0x");
|
|
}
|
|
break;
|
|
|
|
case QSE_T('o'):
|
|
fmt_flags |= 8;
|
|
fmt_uint = 1;
|
|
if (flags & FLAG_HASH)
|
|
{
|
|
/* Force a leading zero digit including zero.
|
|
* 0 with FLAG_HASH and precision 0 still emits '0'.
|
|
* On the contrary, 'X' and 'x' emit no digits
|
|
* for 0 with FLAG_HASH and precision 0. */
|
|
fmt_flags |= QSE_FMTINTMAX_ZEROLEAD;
|
|
}
|
|
break;
|
|
|
|
case QSE_T('u'):
|
|
fmt_uint = 1;
|
|
default:
|
|
fmt_flags |= 10;
|
|
if (flags & FLAG_PLUS)
|
|
fmt_flags |= QSE_FMTINTMAX_PLUSSIGN;
|
|
if (flags & FLAG_SPACE)
|
|
fmt_flags |= QSE_FMTINTMAX_EMPTYSIGN;
|
|
break;
|
|
}
|
|
|
|
|
|
if (wp[WP_WIDTH] > 0)
|
|
{
|
|
if (wp[WP_WIDTH] > rtx->format.tmp.len)
|
|
GROW_WITH_INC (&rtx->format.tmp, wp[WP_WIDTH] - rtx->format.tmp.len);
|
|
fmt_width = wp[WP_WIDTH];
|
|
}
|
|
else fmt_width = rtx->format.tmp.len;
|
|
|
|
do
|
|
{
|
|
if (fmt_uint)
|
|
{
|
|
/* Explicit type-casting for 'l' from qse_awk_int_t
|
|
* to qse_awk_uint_t is needed before passing it to
|
|
* qse_fmtuintmax().
|
|
*
|
|
* Consider a value of -1 for example.
|
|
* -1 is a value with all bits set.
|
|
* If qse_awk_int_t is 4 bytes and qse_uintmax_t
|
|
* is 8 bytes, the value is shown below for
|
|
* each type respectively .
|
|
* -1 - 0xFFFFFFFF (qse_awk_int_t)
|
|
* -1 - 0xFFFFFFFFFFFFFFFF (qse_uintmax_t)
|
|
* Implicit typecasting of -1 from qse_awk_int_t to
|
|
* to qse_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
|
|
* though 0xFFFFFFF is expected in hexadecimal.
|
|
*/
|
|
n = qse_fmtuintmax (
|
|
rtx->format.tmp.ptr,
|
|
fmt_width,
|
|
(qse_awk_uint_t)l,
|
|
fmt_flags,
|
|
wp[WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
else
|
|
{
|
|
n = qse_fmtintmax (
|
|
rtx->format.tmp.ptr,
|
|
fmt_width,
|
|
l,
|
|
fmt_flags,
|
|
wp[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 (fmt[i] == QSE_T('e') || fmt[i] == QSE_T('E') ||
|
|
fmt[i] == QSE_T('g') || fmt[i] == QSE_T('G') ||
|
|
fmt[i] == QSE_T('f'))
|
|
{
|
|
qse_awk_val_t* v;
|
|
qse_awk_flt_t r;
|
|
int n;
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX)
|
|
FMT_CHAR (QSE_T('j'));
|
|
#else
|
|
FMT_CHAR (QSE_T('z'));
|
|
#endif
|
|
FMT_CHAR (fmt[i]);
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg(rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoflt (rtx, v, &r);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
if (qse_str_fcat (out, QSE_STR_PTR(fbu), r) == (qse_size_t)-1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
else if (fmt[i] == QSE_T('c'))
|
|
{
|
|
qse_char_t ch;
|
|
qse_size_t ch_len;
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case QSE_AWK_VAL_NIL:
|
|
ch = QSE_T('\0');
|
|
ch_len = 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_INT:
|
|
ch = (qse_char_t)QSE_AWK_RTX_GETINTFROMVAL (rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_FLT:
|
|
ch = (qse_char_t)((qse_awk_val_flt_t*)v)->val;
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_STR:
|
|
ch = (((qse_awk_val_str_t*)v)->val.len > 0)? ((qse_awk_val_str_t*)v)->val.ptr[0]: '\0';
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
ch = (((qse_awk_val_mbs_t*)v)->val.len > 0)? ((qse_awk_val_mbs_t*)v)->val.ptr[0]: '\0';
|
|
ch_len = 1;
|
|
break;
|
|
|
|
default:
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_EVALTOCHR);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] == 0 || wp[WP_PRECISION] > (qse_awk_int_t)ch_len)
|
|
{
|
|
wp[WP_PRECISION] = (qse_awk_int_t)ch_len;
|
|
}
|
|
|
|
if (wp[WP_PRECISION] > wp[WP_WIDTH]) wp[WP_WIDTH] = wp[WP_PRECISION];
|
|
|
|
if (!(flags & FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_str_ccat (out, QSE_T(' ')) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (wp[WP_PRECISION] > 0)
|
|
{
|
|
if (qse_str_ccat (out, ch) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_str_ccat (out, QSE_T(' ')) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
}
|
|
else if (fmt[i] == QSE_T('s') || fmt[i] == QSE_T('k') || fmt[i] == QSE_T('K'))
|
|
{
|
|
qse_char_t* str_ptr, * str_free = QSE_NULL;
|
|
qse_size_t str_len;
|
|
qse_awk_int_t k;
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
int bytetostr_flagged_radix = 16;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case QSE_AWK_VAL_NIL:
|
|
str_ptr = QSE_T("");
|
|
str_len = 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_STR:
|
|
str_ptr = ((qse_awk_val_str_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_str_t*)v)->val.len;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
str_ptr = ((qse_awk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_mbs_t*)v)->val.len;
|
|
break;
|
|
#else
|
|
if (fmt[i] != QSE_T('s'))
|
|
{
|
|
str_ptr = (qse_char_t*)((qse_awk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_mbs_t*)v)->val.len;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
if (v == val)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_EFMTCNV);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str_ptr = qse_awk_rtx_valtostrdup(rtx, v, &str_len);
|
|
if (!str_ptr)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str_free = str_ptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] > (qse_awk_int_t)str_len) wp[WP_PRECISION] = (qse_awk_int_t)str_len;
|
|
if (wp[WP_PRECISION] > wp[WP_WIDTH]) wp[WP_WIDTH] = wp[WP_PRECISION];
|
|
|
|
if (!(flags & FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_str_ccat(out, QSE_T(' ')) == -1)
|
|
{
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (fmt[i] == QSE_T('k')) bytetostr_flagged_radix |= QSE_BYTETOSTR_LOWERCASE;
|
|
|
|
for (k = 0; k < wp[WP_PRECISION]; k++)
|
|
{
|
|
qse_char_t curc;
|
|
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
curc = str_ptr[k];
|
|
#else
|
|
if (vtype == QSE_AWK_VAL_MBS && fmt[i] != QSE_T('s'))
|
|
curc = (qse_uint8_t)((qse_mchar_t*)str_ptr)[k];
|
|
else curc = str_ptr[k];
|
|
#endif
|
|
|
|
if (fmt[i] != QSE_T('s') && !QSE_AWK_BYTE_PRINTABLE(curc))
|
|
{
|
|
qse_char_t xbuf[3];
|
|
if (curc <= 0xFF)
|
|
{
|
|
if (qse_str_ncat(out, QSE_T("\\x"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr(curc, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
else if (curc <= 0xFFFF)
|
|
{
|
|
qse_uint16_t u16 = curc;
|
|
if (qse_str_ncat(out, QSE_T("\\u"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr((u16 >> 8) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr(u16 & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
else
|
|
{
|
|
qse_uint32_t u32 = curc;
|
|
if (qse_str_ncat(out, QSE_T("\\U"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr((u32 >> 24) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr((u32 >> 16) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr((u32 >> 8) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetostr(u32 & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetostr_flagged_radix, QSE_T('0'));
|
|
if (qse_str_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qse_str_ccat(out, curc) == (qse_size_t)-1)
|
|
{
|
|
s_fail:
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_str_ccat(out, QSE_T(' ')) == (qse_size_t)-1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
}
|
|
else
|
|
{
|
|
if (fmt[i] != QSE_T('%')) OUT_STR (QSE_STR_PTR(fbu), QSE_STR_LEN(fbu));
|
|
OUT_CHAR (fmt[i]);
|
|
goto skip_taking_arg;
|
|
}
|
|
|
|
if (args == QSE_NULL || val != QSE_NULL) stack_arg_idx++;
|
|
else args = args->next;
|
|
skip_taking_arg:
|
|
qse_str_clear (fbu);
|
|
}
|
|
|
|
/* flush uncompleted formatting sequence */
|
|
OUT_STR (QSE_STR_PTR(fbu), QSE_STR_LEN(fbu));
|
|
|
|
*len = QSE_STR_LEN(out);
|
|
return QSE_STR_PTR(out);
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
qse_mchar_t* qse_awk_rtx_formatmbs (
|
|
qse_awk_rtx_t* rtx, qse_mbs_t* out, qse_mbs_t* fbu,
|
|
const qse_mchar_t* fmt, qse_size_t fmt_len,
|
|
qse_size_t nargs_on_stack, qse_awk_nde_t* args, qse_size_t* len)
|
|
{
|
|
qse_size_t i;
|
|
qse_size_t stack_arg_idx = 1;
|
|
qse_awk_val_t* val;
|
|
|
|
#define OUT_MCHAR(c) QSE_BLOCK( \
|
|
if (qse_mbs_ccat(out, (c)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define OUT_MBS(ptr,len) QSE_BLOCK( \
|
|
if (qse_mbs_ncat(out, (ptr), (len)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define FMT_MCHAR(c) QSE_BLOCK( \
|
|
if (qse_mbs_ccat(fbu, (c)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define FMT_MBS(ptr,len) QSE_BLOCK( \
|
|
if (qse_mbs_ncat(fbu, (ptr), (len)) == (qse_size_t)-1) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define GROW_MBSBUF(buf) QSE_BLOCK( \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
qse_awk_rtx_freemem (rtx, (buf)->ptr); \
|
|
(buf)->ptr = QSE_NULL; \
|
|
} \
|
|
(buf)->len += (buf)->inc; \
|
|
(buf)->ptr = (qse_mchar_t*)qse_awk_rtx_allocmem(rtx, (buf)->len * QSE_SIZEOF(qse_mchar_t)); \
|
|
if ((buf)->ptr == QSE_NULL) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
(buf)->len = 0; \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
#define GROW_MBSBUF_WITH_INC(buf,incv) QSE_BLOCK( \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
qse_awk_rtx_freemem (rtx, (buf)->ptr); \
|
|
(buf)->ptr = QSE_NULL; \
|
|
} \
|
|
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
|
|
(buf)->ptr = (qse_mchar_t*)qse_awk_rtx_allocmem(rtx, (buf)->len * QSE_SIZEOF(qse_mchar_t)); \
|
|
if ((buf)->ptr == QSE_NULL) \
|
|
{ \
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM); \
|
|
(buf)->len = 0; \
|
|
return QSE_NULL; \
|
|
} \
|
|
)
|
|
|
|
QSE_ASSERTX (rtx->formatmbs.tmp.ptr != QSE_NULL,
|
|
"run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called");
|
|
|
|
if (nargs_on_stack == (qse_size_t)-1)
|
|
{
|
|
val = (qse_awk_val_t*)args;
|
|
nargs_on_stack = 2;
|
|
}
|
|
else
|
|
{
|
|
val = QSE_NULL;
|
|
}
|
|
|
|
if (out == QSE_NULL) out = &rtx->formatmbs.out;
|
|
if (fbu == QSE_NULL) fbu = &rtx->formatmbs.fmt;
|
|
|
|
qse_mbs_clear (out);
|
|
qse_mbs_clear (fbu);
|
|
|
|
for (i = 0; i < fmt_len; i++)
|
|
{
|
|
qse_awk_int_t wp[2];
|
|
int wp_idx;
|
|
#define WP_WIDTH 0
|
|
#define WP_PRECISION 1
|
|
|
|
int flags;
|
|
#define FLAG_SPACE (1 << 0)
|
|
#define FLAG_HASH (1 << 1)
|
|
#define FLAG_ZERO (1 << 2)
|
|
#define FLAG_PLUS (1 << 3)
|
|
#define FLAG_MINUS (1 << 4)
|
|
|
|
if (QSE_MBS_LEN(fbu) == 0)
|
|
{
|
|
/* format specifier is empty */
|
|
if (fmt[i] == QSE_MT('%'))
|
|
{
|
|
/* add % to format specifier (fbu) */
|
|
FMT_MCHAR (fmt[i]);
|
|
}
|
|
else
|
|
{
|
|
/* normal output */
|
|
OUT_MCHAR (fmt[i]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* handle flags */
|
|
flags = 0;
|
|
while (i < fmt_len)
|
|
{
|
|
switch (fmt[i])
|
|
{
|
|
case QSE_MT(' '):
|
|
flags |= FLAG_SPACE;
|
|
break;
|
|
case QSE_MT('#'):
|
|
flags |= FLAG_HASH;
|
|
break;
|
|
case QSE_MT('0'):
|
|
flags |= FLAG_ZERO;
|
|
break;
|
|
case QSE_MT('+'):
|
|
flags |= FLAG_PLUS;
|
|
break;
|
|
case QSE_MT('-'):
|
|
flags |= FLAG_MINUS;
|
|
break;
|
|
default:
|
|
goto wp_mod_init;
|
|
}
|
|
|
|
FMT_MCHAR (fmt[i]); i++;
|
|
}
|
|
|
|
wp_mod_init:
|
|
wp[WP_WIDTH] = -1; /* width */
|
|
wp[WP_PRECISION] = -1; /* precision */
|
|
wp_idx = WP_WIDTH; /* width first */
|
|
|
|
wp_mod_main:
|
|
if (i < fmt_len && fmt[i] == QSE_MT('*'))
|
|
{
|
|
/* variable width/precision modifier.
|
|
* take the width/precision from a parameter and
|
|
* transform it to a fixed length format */
|
|
qse_awk_val_t* v;
|
|
int n;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg(rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression(rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint(rtx, v, &wp[wp_idx]);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
do
|
|
{
|
|
n = qse_fmtintmaxtombs (
|
|
rtx->formatmbs.tmp.ptr,
|
|
rtx->formatmbs.tmp.len,
|
|
wp[wp_idx],
|
|
10 | QSE_FMTINTMAX_NOTRUNC | QSE_FMTINTMAX_NONULL,
|
|
-1,
|
|
QSE_MT('\0'),
|
|
QSE_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 == QSE_NULL || val != QSE_NULL) stack_arg_idx++;
|
|
else args = args->next;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* fixed width/precision modifier */
|
|
if (i < fmt_len && QSE_AWK_ISMDIGIT(rtx->awk, fmt[i]))
|
|
{
|
|
wp[wp_idx] = 0;
|
|
do
|
|
{
|
|
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - QSE_MT('0');
|
|
FMT_MCHAR (fmt[i]); i++;
|
|
}
|
|
while (i < fmt_len && QSE_AWK_ISMDIGIT(rtx->awk, fmt[i]));
|
|
}
|
|
}
|
|
|
|
if (wp_idx == WP_WIDTH && i < fmt_len && fmt[i] == QSE_MT('.'))
|
|
{
|
|
wp[WP_PRECISION] = 0;
|
|
FMT_MCHAR (fmt[i]); i++;
|
|
|
|
wp_idx = WP_PRECISION; /* change index to precision */
|
|
goto wp_mod_main;
|
|
}
|
|
|
|
if (i >= fmt_len) break;
|
|
|
|
if (fmt[i] == QSE_MT('d') || fmt[i] == QSE_MT('i') ||
|
|
fmt[i] == QSE_MT('x') || fmt[i] == QSE_MT('X') ||
|
|
fmt[i] == QSE_MT('b') || fmt[i] == QSE_MT('B') ||
|
|
fmt[i] == QSE_MT('o'))
|
|
{
|
|
qse_awk_val_t* v;
|
|
qse_awk_int_t l;
|
|
int n;
|
|
|
|
int fmt_flags;
|
|
int fmt_uint = 0;
|
|
int fmt_width;
|
|
qse_mchar_t fmt_fill = QSE_MT('\0');
|
|
const qse_mchar_t* fmt_prefix = QSE_NULL;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoint (rtx, v, &l);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
fmt_flags = QSE_FMTINTMAX_NOTRUNC | QSE_FMTINTMAX_NONULL;
|
|
|
|
if (l == 0 && wp[WP_PRECISION] == 0)
|
|
{
|
|
/* A zero value with a precision of zero produces
|
|
* no character. */
|
|
fmt_flags |= QSE_FMTINTMAX_NOZERO;
|
|
}
|
|
|
|
if (wp[WP_WIDTH] != -1)
|
|
{
|
|
QSE_ASSERTX (wp[WP_WIDTH] > 0, "Width must be greater than 0 if specified");
|
|
|
|
/* justification when width is specified. */
|
|
if (flags & FLAG_ZERO)
|
|
{
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* FLAG_MINUS wins if both FLAG_ZERO
|
|
* and FLAG_MINUS are specified. */
|
|
fmt_fill = QSE_MT(' ');
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= QSE_FMTINTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wp[WP_PRECISION] == -1)
|
|
{
|
|
/* precision not specified.
|
|
* FLAG_ZERO can take effect */
|
|
fmt_fill = QSE_MT('0');
|
|
fmt_flags |= QSE_FMTINTMAX_FILLCENTER;
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = QSE_MT(' ');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = QSE_MT(' ');
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= QSE_FMTINTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (fmt[i])
|
|
{
|
|
case QSE_MT('B'):
|
|
case QSE_MT('b'):
|
|
fmt_flags |= 2;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0b */
|
|
fmt_prefix = QSE_MT("0b");
|
|
}
|
|
break;
|
|
|
|
case QSE_MT('X'):
|
|
fmt_flags |= QSE_FMTINTMAX_UPPERCASE;
|
|
case QSE_MT('x'):
|
|
fmt_flags |= 16;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0x */
|
|
fmt_prefix = QSE_MT("0x");
|
|
}
|
|
break;
|
|
|
|
case QSE_MT('o'):
|
|
fmt_flags |= 8;
|
|
fmt_uint = 1;
|
|
if (flags & FLAG_HASH)
|
|
{
|
|
/* Force a leading zero digit including zero.
|
|
* 0 with FLAG_HASH and precision 0 still emits '0'.
|
|
* On the contrary, 'X' and 'x' emit no digits
|
|
* for 0 with FLAG_HASH and precision 0. */
|
|
fmt_flags |= QSE_FMTINTMAX_ZEROLEAD;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fmt_flags |= 10;
|
|
if (flags & FLAG_PLUS)
|
|
fmt_flags |= QSE_FMTINTMAX_PLUSSIGN;
|
|
if (flags & FLAG_SPACE)
|
|
fmt_flags |= QSE_FMTINTMAX_EMPTYSIGN;
|
|
break;
|
|
}
|
|
|
|
|
|
if (wp[WP_WIDTH] > 0)
|
|
{
|
|
if (wp[WP_WIDTH] > rtx->formatmbs.tmp.len)
|
|
GROW_MBSBUF_WITH_INC (&rtx->formatmbs.tmp, wp[WP_WIDTH] - rtx->formatmbs.tmp.len);
|
|
fmt_width = wp[WP_WIDTH];
|
|
}
|
|
else fmt_width = rtx->formatmbs.tmp.len;
|
|
|
|
do
|
|
{
|
|
if (fmt_uint)
|
|
{
|
|
/* Explicit type-casting for 'l' from qse_awk_int_t
|
|
* to qse_awk_uint_t is needed before passing it to
|
|
* qse_fmtuintmax().
|
|
*
|
|
* Consider a value of -1 for example.
|
|
* -1 is a value with all bits set.
|
|
* If qse_awk_int_t is 4 bytes and qse_uintmax_t
|
|
* is 8 bytes, the value is shown below for
|
|
* each type respectively .
|
|
* -1 - 0xFFFFFFFF (qse_awk_int_t)
|
|
* -1 - 0xFFFFFFFFFFFFFFFF (qse_uintmax_t)
|
|
* Implicit typecasting of -1 from qse_awk_int_t to
|
|
* to qse_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
|
|
* though 0xFFFFFFF is expected in hexadecimal.
|
|
*/
|
|
n = qse_fmtuintmaxtombs (
|
|
rtx->formatmbs.tmp.ptr,
|
|
fmt_width,
|
|
(qse_awk_uint_t)l,
|
|
fmt_flags,
|
|
wp[WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
else
|
|
{
|
|
n = qse_fmtintmaxtombs (
|
|
rtx->formatmbs.tmp.ptr,
|
|
fmt_width,
|
|
l,
|
|
fmt_flags,
|
|
wp[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 (fmt[i] == QSE_MT('e') || fmt[i] == QSE_MT('E') ||
|
|
fmt[i] == QSE_MT('g') || fmt[i] == QSE_MT('G') ||
|
|
fmt[i] == QSE_MT('f'))
|
|
{
|
|
qse_awk_val_t* v;
|
|
qse_awk_flt_t r;
|
|
int n;
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX)
|
|
FMT_MCHAR (QSE_MT('j'));
|
|
#else
|
|
FMT_MCHAR (QSE_MT('z'));
|
|
#endif
|
|
FMT_MCHAR (fmt[i]);
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg(rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression(rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
n = qse_awk_rtx_valtoflt(rtx, v, &r);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
if (n <= -1) return QSE_NULL;
|
|
|
|
if (qse_mbs_fcat (out, QSE_MBS_PTR(fbu), r) == (qse_size_t)-1)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
else if (fmt[i] == QSE_MT('c'))
|
|
{
|
|
qse_mchar_t ch;
|
|
qse_size_t ch_len;
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg(rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val != QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression(rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
vtype = QSE_AWK_RTX_GETVALTYPE (rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case QSE_AWK_VAL_NIL:
|
|
ch = QSE_MT('\0');
|
|
ch_len = 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_INT:
|
|
ch = (qse_mchar_t)QSE_AWK_RTX_GETINTFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_FLT:
|
|
ch = (qse_mchar_t)((qse_awk_val_flt_t*)v)->val;
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_STR:
|
|
ch_len = ((qse_awk_val_str_t*)v)->val.len;
|
|
if (ch_len > 0)
|
|
{
|
|
/* value truncation is expected */
|
|
ch = ((qse_awk_val_str_t*)v)->val.ptr[0];
|
|
ch_len = 1;
|
|
}
|
|
else ch = QSE_MT('\0');
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
ch_len = ((qse_awk_val_mbs_t*)v)->val.len;
|
|
if (ch_len > 0)
|
|
{
|
|
ch = ((qse_awk_val_mbs_t*)v)->val.ptr[0];
|
|
ch_len = 1;
|
|
}
|
|
else ch = QSE_MT('\0');
|
|
break;
|
|
|
|
default:
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_EVALTOCHR);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] == 0 || wp[WP_PRECISION] > (qse_awk_int_t)ch_len)
|
|
{
|
|
wp[WP_PRECISION] = (qse_awk_int_t)ch_len;
|
|
}
|
|
|
|
if (wp[WP_PRECISION] > wp[WP_WIDTH]) wp[WP_WIDTH] = wp[WP_PRECISION];
|
|
|
|
if (!(flags & FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_mbs_ccat (out, QSE_MT(' ')) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (wp[WP_PRECISION] > 0)
|
|
{
|
|
if (qse_mbs_ccat(out, ch) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_mbs_ccat (out, QSE_MT(' ')) == -1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
}
|
|
else if (fmt[i] == QSE_MT('s') || fmt[i] == QSE_MT('k') || fmt[i] == QSE_MT('K'))
|
|
{
|
|
qse_mchar_t* str_ptr, * str_free = QSE_NULL;
|
|
qse_size_t str_len;
|
|
qse_awk_int_t k;
|
|
qse_awk_val_t* v;
|
|
qse_awk_val_type_t vtype;
|
|
int bytetombs_flagged_radix = 16;
|
|
|
|
if (args == QSE_NULL)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = qse_awk_rtx_getarg (rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
if (val)
|
|
{
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
SETERR_COD (rtx, QSE_AWK_EFMTARG);
|
|
return QSE_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression (rtx, args);
|
|
if (v == QSE_NULL) return QSE_NULL;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refupval (rtx, v);
|
|
|
|
vtype = QSE_AWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case QSE_AWK_VAL_NIL:
|
|
str_ptr = QSE_MT("");
|
|
str_len = 0;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_MBS:
|
|
str_ptr = ((qse_awk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_mbs_t*)v)->val.len;
|
|
break;
|
|
|
|
case QSE_AWK_VAL_STR:
|
|
#if defined(QSE_CHAR_IS_MCHAR)
|
|
str_ptr = ((qse_awk_val_str_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_str_t*)v)->val.len;
|
|
break;
|
|
#else
|
|
if (fmt[i] != QSE_MT('s'))
|
|
{
|
|
/* arrange to print the wide character string byte by byte regardless of byte order */
|
|
str_ptr = (qse_mchar_t*)((qse_awk_val_str_t*)v)->val.ptr;
|
|
str_len = ((qse_awk_val_str_t*)v)->val.len * (QSE_SIZEOF_CHAR_T / QSE_SIZEOF_MCHAR_T);
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
if (v == val)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_EFMTCNV);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str_ptr = qse_awk_rtx_valtombsdup(rtx, v, &str_len);
|
|
if (!str_ptr)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
return QSE_NULL;
|
|
}
|
|
|
|
str_free = str_ptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] > (qse_awk_int_t)str_len) wp[WP_PRECISION] = (qse_awk_int_t)str_len;
|
|
if (wp[WP_PRECISION] > wp[WP_WIDTH]) wp[WP_WIDTH] = wp[WP_PRECISION];
|
|
|
|
if (!(flags & FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_mbs_ccat(out, QSE_MT(' ')) == -1)
|
|
{
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (fmt[i] == QSE_MT('k')) bytetombs_flagged_radix |= QSE_BYTETOSTR_LOWERCASE;
|
|
|
|
for (k = 0; k < wp[WP_PRECISION]; k++)
|
|
{
|
|
qse_mchar_t curc;
|
|
|
|
curc = str_ptr[k];
|
|
|
|
if (fmt[i] != QSE_MT('s') && !QSE_AWK_BYTE_PRINTABLE(curc))
|
|
{
|
|
qse_mchar_t xbuf[3];
|
|
if (curc <= 0xFF)
|
|
{
|
|
if (qse_mbs_ncat(out, QSE_MT("\\x"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs(curc, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
else if (curc <= 0xFFFF)
|
|
{
|
|
qse_uint16_t u16 = curc;
|
|
if (qse_mbs_ncat(out, QSE_MT("\\u"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs((u16 >> 8) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs(u16 & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
else
|
|
{
|
|
qse_uint32_t u32 = curc;
|
|
if (qse_mbs_ncat(out, QSE_MT("\\U"), 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs((u32 >> 24) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs((u32 >> 16) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs((u32 >> 8) & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
qse_bytetombs(u32 & 0xFF, xbuf, QSE_COUNTOF(xbuf), bytetombs_flagged_radix, QSE_MT('0'));
|
|
if (qse_mbs_ncat(out, xbuf, 2) == (qse_size_t)-1) goto s_fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qse_mbs_ccat(out, curc) == (qse_size_t)-1)
|
|
{
|
|
s_fail:
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (str_free) qse_awk_rtx_freemem (rtx, str_free);
|
|
|
|
if (flags & FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[WP_WIDTH] > wp[WP_PRECISION])
|
|
{
|
|
if (qse_mbs_ccat(out, QSE_MT(' ')) == (qse_size_t)-1)
|
|
{
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
SETERR_COD (rtx, QSE_AWK_ENOMEM);
|
|
return QSE_NULL;
|
|
}
|
|
wp[WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
qse_awk_rtx_refdownval (rtx, v);
|
|
}
|
|
else
|
|
{
|
|
if (fmt[i] != QSE_MT('%')) OUT_MBS (QSE_MBS_PTR(fbu), QSE_MBS_LEN(fbu));
|
|
OUT_MCHAR (fmt[i]);
|
|
goto skip_taking_arg;
|
|
}
|
|
|
|
if (args == QSE_NULL || val != QSE_NULL) stack_arg_idx++;
|
|
else args = args->next;
|
|
skip_taking_arg:
|
|
qse_mbs_clear (fbu);
|
|
}
|
|
|
|
/* flush uncompleted formatting sequence */
|
|
OUT_MBS (QSE_MBS_PTR(fbu), QSE_MBS_LEN(fbu));
|
|
|
|
*len = QSE_MBS_LEN(out);
|
|
return QSE_MBS_PTR(out);
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
|
|
void qse_awk_rtx_setnrflt (qse_awk_rtx_t* rtx, const qse_awk_nrflt_t* nrflt)
|
|
{
|
|
rtx->nrflt = *nrflt;
|
|
}
|
|
|
|
void qse_awk_rtx_getnrflt (qse_awk_rtx_t* rtx, qse_awk_nrflt_t* nrflt)
|
|
{
|
|
*nrflt = rtx->nrflt;
|
|
}
|