hawk/hawk/lib/run.c
hyung-hwan b7fecc3172 added a new global variable BRS for byte reading.
wrote some more functions to support byte reading
2020-01-14 14:55:34 +00:00

8793 lines
214 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 "hawk-prv.h"
#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 hawk_int_t is very large */
#if (HAWK_SIZEOF_INT_T <= 16) /* 128 bits */
# define IDXBUFSIZE 64
#elif (HAWK_SIZEOF_INT_T <= 32) /* 256 bits */
# define IDXBUFSIZE 128
#elif (HAWK_SIZEOF_INT_T <= 64) /* 512 bits */
# define IDXBUFSIZE 192
#elif (HAWK_SIZEOF_INT_T <= 128) /* 1024 bits */
# define IDXBUFSIZE 384
#elif (HAWK_SIZEOF_INT_T <= 256) /* 2048 bits */
# define IDXBUFSIZE 640
#else
# error unsupported. hawk_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
{
hawk_val_t** args;
hawk_oow_t nargs;
const hawk_ooch_t* argspec;
};
#define DEFAULT_CONVFMT HAWK_T("%.6g")
#define DEFAULT_OFMT HAWK_T("%.6g")
#define DEFAULT_OFS HAWK_T(" ")
#define DEFAULT_ORS HAWK_T("\n")
#define DEFAULT_ORS_CRLF HAWK_T("\r\n")
#define DEFAULT_SUBSEP HAWK_T("\034")
/* the index of a positional variable should be a positive interger
* equal to or less than the maximum value of the type by which
* the index is represented. but it has an extra check against the
* maximum value of hawk_oow_t as the reference is represented
* in a pointer variable of hawk_val_ref_t and sizeof(void*) is
* equal to sizeof(hawk_oow_t). */
#define IS_VALID_POSIDX(idx) \
((idx) >= 0 && \
(idx) <= HAWK_TYPE_MAX(hawk_int_t) && \
(idx) <= HAWK_TYPE_MAX(hawk_oow_t))
#define CLRERR(rtx) hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ENOERR)
#define ADJERR_LOC(rtx,l) do { (rtx)->_gem.errloc = *(l); } while (0)
static hawk_oow_t push_arg_from_vals (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, void* data);
static hawk_oow_t push_arg_from_nde (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, void* data);
static int init_rtx (hawk_rtx_t* rtx, hawk_t* awk, hawk_rio_cbs_t* rio);
static void fini_rtx (hawk_rtx_t* rtx, int fini_globals);
static int init_globals (hawk_rtx_t* rtx);
static void refdown_globals (hawk_rtx_t* run, int pop);
static int run_pblocks (hawk_rtx_t* rtx);
static int run_pblock_chain (hawk_rtx_t* rtx, hawk_chain_t* cha);
static int run_pblock (hawk_rtx_t* rtx, hawk_chain_t* cha, hawk_oow_t bno);
static int run_block (hawk_rtx_t* rtx, hawk_nde_blk_t* nde);
static int run_statement (hawk_rtx_t* rtx, hawk_nde_t* nde);
static int run_if (hawk_rtx_t* rtx, hawk_nde_if_t* nde);
static int run_while (hawk_rtx_t* rtx, hawk_nde_while_t* nde);
static int run_for (hawk_rtx_t* rtx, hawk_nde_for_t* nde);
static int run_foreach (hawk_rtx_t* rtx, hawk_nde_foreach_t* nde);
static int run_break (hawk_rtx_t* rtx, hawk_nde_break_t* nde);
static int run_continue (hawk_rtx_t* rtx, hawk_nde_continue_t* nde);
static int run_return (hawk_rtx_t* rtx, hawk_nde_return_t* nde);
static int run_exit (hawk_rtx_t* rtx, hawk_nde_exit_t* nde);
static int run_next (hawk_rtx_t* rtx, hawk_nde_next_t* nde);
static int run_nextfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde);
static int run_delete (hawk_rtx_t* rtx, hawk_nde_delete_t* nde);
static int run_reset (hawk_rtx_t* rtx, hawk_nde_reset_t* nde);
static int run_print (hawk_rtx_t* rtx, hawk_nde_print_t* nde);
static int run_printf (hawk_rtx_t* rtx, hawk_nde_print_t* nde);
static int output_formatted (
hawk_rtx_t* run, int out_type, const hawk_ooch_t* dst,
const hawk_ooch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args);
static int output_formatted_bytes (
hawk_rtx_t* run, int out_type, const hawk_ooch_t* dst,
const hawk_bch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args);
static hawk_val_t* eval_expression (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_expression0 (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_group (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_assignment (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* do_assignment (hawk_rtx_t* rtx, hawk_nde_t* var, hawk_val_t* val);
static hawk_val_t* do_assignment_nonidx (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val);
static hawk_val_t* do_assignment_idx (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val);
static hawk_val_t* do_assignment_pos (hawk_rtx_t* rtx, hawk_nde_pos_t* pos, hawk_val_t* val);
static hawk_val_t* eval_binary (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_binop_lor (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
static hawk_val_t* eval_binop_land (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
static hawk_val_t* eval_binop_in (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
static hawk_val_t* eval_binop_bor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_bxor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_band (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_teq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_tne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_eq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_ne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_gt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_ge (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_lt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_le (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_lshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_rshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_plus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_minus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_mul (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_div (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_idiv (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_mod (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_exp (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_concat (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
static hawk_val_t* eval_binop_ma (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
static hawk_val_t* eval_binop_nm (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
static hawk_val_t* eval_unary (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_incpre (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_incpst (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_cnd (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_fncall_fun_ex (hawk_rtx_t* rtx, hawk_nde_t* nde, void(*errhandler)(void*), void* eharg);
static hawk_val_t* eval_fncall_fnc (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_fncall_fun (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_fncall_var (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* __eval_call (
hawk_rtx_t* rtx,
hawk_nde_t* nde,
hawk_fun_t* fun,
hawk_oow_t(*argpusher)(hawk_rtx_t*,hawk_nde_fncall_t*,void*),
void* apdata, /* data to argpusher */
void(*errhandler)(void*),
void* eharg);
static int get_reference (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_val_t*** ref);
static hawk_val_t** get_reference_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* nde, hawk_val_t** val);
static hawk_val_t* eval_int (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_flt (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_str (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_mbs (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_named (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_gbl (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_lcl (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_arg (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_namedidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_gblidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_lclidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_argidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_pos (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_print (hawk_rtx_t* rtx, hawk_nde_t* nde);
static hawk_val_t* eval_printf (hawk_rtx_t* rtx, hawk_nde_t* nde);
static int __raw_push (hawk_rtx_t* rtx, void* val);
#define __raw_pop(rtx) \
do { \
HAWK_ASSERT ((rtx)->stack_top > (rtx)->stack_base); \
(rtx)->stack_top--; \
} while (0)
static int read_record (hawk_rtx_t* rtx);
static int shorten_record (hawk_rtx_t* rtx, hawk_oow_t nflds);
static hawk_ooch_t* idxnde_to_str (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_ooch_t* buf, hawk_oow_t* len);
typedef hawk_val_t* (*binop_func_t) (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
typedef hawk_val_t* (*eval_expr_t) (hawk_rtx_t* rtx, hawk_nde_t* nde);
HAWK_INLINE hawk_oow_t hawk_rtx_getnargs (hawk_rtx_t* rtx)
{
return (hawk_oow_t) RTX_STACK_NARGS(rtx);
}
HAWK_INLINE hawk_val_t* hawk_rtx_getarg (hawk_rtx_t* rtx, hawk_oow_t idx)
{
return RTX_STACK_ARG(rtx, idx);
}
HAWK_INLINE hawk_val_t* hawk_rtx_getgbl (hawk_rtx_t* rtx, int id)
{
HAWK_ASSERT (id >= 0 && id < (int)HAWK_ARR_SIZE(rtx->awk->parse.gbls));
return RTX_STACK_GBL(rtx, id);
}
const hawk_oocs_t* hawk_rtx_getsubsep (hawk_rtx_t* rtx)
{
return &rtx->gbl.subsep;
}
/* internal function to set a value to a global variable.
* this function can handle a few special global variables that
* require special treatment. */
static int set_global (hawk_rtx_t* rtx, int idx, hawk_nde_var_t* var, hawk_val_t* val, int assign)
{
hawk_val_t* old;
hawk_rtx_ecb_t* ecb;
hawk_val_type_t vtype, old_vtype;
old = RTX_STACK_GBL (rtx, idx);
vtype = HAWK_RTX_GETVALTYPE (rtx, val);
old_vtype = HAWK_RTX_GETVALTYPE (rtx, old);
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP))
{
hawk_errnum_t errnum = HAWK_ENOERR;
const hawk_ooch_t* errfmt;
if (vtype == HAWK_VAL_MAP)
{
if (old_vtype == HAWK_VAL_NIL)
{
/* a nil valul can be overridden with any values */
/* ok. no error */
}
else if (!assign && old_vtype == HAWK_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 = HAWK_ESCALARTOMAP;
errfmt = HAWK_T("not allowed to change a scalar '%.*js' to a map");
}
}
else
{
if (old_vtype == HAWK_VAL_MAP)
{
errnum = HAWK_EMAPTOSCALAR;
errfmt = HAWK_T("not allowed to change a map '%.*js' to a scalar");
}
}
if (errnum != HAWK_ENOERR)
{
/* once a variable becomes a map, it cannot be assigned
* others value than another map. you can only add a member
* using indexing. */
if (var)
{
/* global variable */
hawk_rtx_seterrfmt (rtx, &var->loc, errnum, errfmt, var->id.name.len, var->id.name.ptr);
}
else
{
/* hawk_rtx_setgbl() has been called */
hawk_oocs_t ea;
ea.ptr = (hawk_ooch_t*)hawk_getgblname(hawk_rtx_gethawk(rtx), idx, &ea.len);
hawk_rtx_seterrfmt (rtx, HAWK_NULL, errnum, errfmt, ea.len, ea.ptr);
}
return -1;
}
}
if (vtype == HAWK_VAL_MAP)
{
if (idx >= HAWK_MIN_GBL_ID && idx <= HAWK_MAX_GBL_ID)
{
/* short-circuit check block to prevent the basic built-in
* variables from being assigned a map. if you happen to add
* one and if that's allowed to be a map, you may have to
* change the condition here. */
/* TODO: use global variable attribute. can it be a map? can it be a scalar? is it read-only???? */
hawk_oocs_t ea;
ea.ptr = (hawk_ooch_t*)hawk_getgblname(hawk_rtx_gethawk(rtx), idx, &ea.len);
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_ESCALARTOMAP, HAWK_T("not allowed to change a scalar '%.*js' to a map"), ea.len, ea.ptr);
return -1;
}
}
if (old == val)
{
/* if the old value is the same as the new value, don't take any actions.
* note that several inspections have been performed before this check,
* mainly for consistency. anyway, this condition can be met if you execute
* a statement like 'ARGV=ARGV'. */
return 0;
}
/* perform actual assignment or assignment-like operation */
switch (idx)
{
case HAWK_GBL_BRS:
{
hawk_bcs_t rss;
/* due to the expression evaluation rule, the
* regular expression can not be an assigned
* value */
HAWK_ASSERT (vtype != HAWK_VAL_REX);
rss.ptr = hawk_rtx_getvalbcstr(rtx, val, &rss.len);
if (!rss.ptr) return -1;
if (rtx->gbl.brs[0])
{
hawk_rtx_freerex (rtx, rtx->gbl.brs[0], rtx->gbl.brs[1]);
rtx->gbl.brs[0] = HAWK_NULL;
rtx->gbl.brs[1] = HAWK_NULL;
}
if (rss.len > 1)
{
hawk_tre_t* rex, * irex;
/* compile the regular expression */
/* TODO: mbs buildrex */
if (hawk_rtx_buildrex(rtx, rss.ptr, rss.len, &rex, &irex) <= -1)
{
hawk_rtx_freevalbcstr (rtx, val, rss.ptr);
return -1;
}
rtx->gbl.brs[0] = rex;
rtx->gbl.brs[1] = irex;
}
hawk_rtx_freevalbcstr (rtx, val, rss.ptr);
break;
}
case HAWK_GBL_CONVFMT:
{
hawk_oow_t i;
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr (rtx, val, &out) <= -1)
return -1;
for (i = 0; i < out.u.cpldup.len; i++)
{
if (out.u.cpldup.ptr[i] == HAWK_T('\0'))
{
/* '\0' is included in the value */
hawk_rtx_freemem (rtx, out.u.cpldup.ptr);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_ECONVFMTCHR);
return -1;
}
}
if (rtx->gbl.convfmt.ptr) hawk_rtx_freemem (rtx, rtx->gbl.convfmt.ptr);
rtx->gbl.convfmt.ptr = out.u.cpldup.ptr;
rtx->gbl.convfmt.len = out.u.cpldup.len;
break;
}
case HAWK_GBL_FNR:
{
int n;
hawk_int_t lv;
n = hawk_rtx_valtoint (rtx, val, &lv);
if (n <= -1) return -1;
rtx->gbl.fnr = lv;
break;
}
case HAWK_GBL_FS:
{
hawk_ooch_t* fs_ptr;
hawk_oow_t fs_len;
if (vtype == HAWK_VAL_STR)
{
fs_ptr = ((hawk_val_str_t*)val)->val.ptr;
fs_len = ((hawk_val_str_t*)val)->val.len;
}
else
{
hawk_rtx_valtostr_out_t out;
/* due to the expression evaluation rule, the
* regular expression can not be an assigned value */
HAWK_ASSERT (vtype != HAWK_VAL_REX);
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_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] == HAWK_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. */
hawk_tre_t* rex, * irex;
if (hawk_rtx_buildrex(rtx, fs_ptr, fs_len, &rex, &irex) <= -1)
{
if (vtype != HAWK_VAL_STR) hawk_rtx_freemem (rtx, fs_ptr);
return -1;
}
if (rtx->gbl.fs[0]) hawk_rtx_freerex (rtx, rtx->gbl.fs[0], rtx->gbl.fs[1]);
rtx->gbl.fs[0] = rex;
rtx->gbl.fs[1] = irex;
}
if (vtype != HAWK_VAL_STR) hawk_rtx_freemem (rtx, fs_ptr);
break;
}
case HAWK_GBL_IGNORECASE:
{
hawk_int_t l;
hawk_flt_t r;
int vt;
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
if (vt <= -1) return -1;
if (vt == 0)
rtx->gbl.ignorecase = ((l > 0)? 1: (l < 0)? -1: 0);
else
rtx->gbl.ignorecase = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
break;
}
case HAWK_GBL_NF:
{
int n;
hawk_int_t lv;
n = hawk_rtx_valtoint(rtx, val, &lv);
if (n <= -1) return -1;
if (lv < (hawk_int_t)rtx->inrec.nflds)
{
if (shorten_record(rtx, (hawk_oow_t)lv) == -1)
{
/* adjust the error line */
/*if (var) ADJERR_LOC (rtx, &var->loc);*/
return -1;
}
}
break;
}
case HAWK_GBL_NR:
{
int n;
hawk_int_t lv;
n = hawk_rtx_valtoint(rtx, val, &lv);
if (n <= -1) return -1;
rtx->gbl.nr = lv;
break;
}
case HAWK_GBL_OFMT:
{
hawk_oow_t i;
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
for (i = 0; i < out.u.cpldup.len; i++)
{
if (out.u.cpldup.ptr[i] == HAWK_T('\0'))
{
hawk_rtx_freemem (rtx, out.u.cpldup.ptr);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOFMTCHR);
return -1;
}
}
if (rtx->gbl.ofmt.ptr) hawk_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 HAWK_GBL_OFS:
{
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
if (rtx->gbl.ofs.ptr) hawk_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 HAWK_GBL_ORS:
{
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
if (rtx->gbl.ors.ptr) hawk_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 HAWK_GBL_RS:
{
hawk_oocs_t rss;
/* due to the expression evaluation rule, the
* regular expression can not be an assigned
* value */
HAWK_ASSERT (vtype != HAWK_VAL_REX);
rss.ptr = hawk_rtx_getvaloocstr(rtx, val, &rss.len);
if (!rss.ptr) return -1;
if (rtx->gbl.rs[0])
{
hawk_rtx_freerex (rtx, rtx->gbl.rs[0], rtx->gbl.rs[1]);
rtx->gbl.rs[0] = HAWK_NULL;
rtx->gbl.rs[1] = HAWK_NULL;
}
if (rss.len > 1)
{
hawk_tre_t* rex, * irex;
/* compile the regular expression */
if (hawk_rtx_buildrex(rtx, rss.ptr, rss.len, &rex, &irex) <= -1)
{
hawk_rtx_freevaloocstr (rtx, val, rss.ptr);
return -1;
}
rtx->gbl.rs[0] = rex;
rtx->gbl.rs[1] = irex;
}
hawk_rtx_freevaloocstr (rtx, val, rss.ptr);
break;
}
case HAWK_GBL_STRIPRECSPC:
{
hawk_int_t l;
hawk_flt_t r;
int vt;
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
if (vt <= -1) return -1;
if (vt == 0)
rtx->gbl.striprecspc = ((l > 0)? 1: (l < 0)? -1: 0);
else
rtx->gbl.striprecspc = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
break;
}
case HAWK_GBL_SUBSEP:
{
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, val, &out) <= -1) return -1;
if (rtx->gbl.subsep.ptr) hawk_rtx_freemem (rtx, rtx->gbl.subsep.ptr);
rtx->gbl.subsep.ptr = out.u.cpldup.ptr;
rtx->gbl.subsep.len = out.u.cpldup.len;
break;
}
}
hawk_rtx_refdownval (rtx, old);
RTX_STACK_GBL(rtx,idx) = val;
hawk_rtx_refupval (rtx, val);
for (ecb = (rtx)->ecb; ecb; ecb = ecb->next)
{
if (ecb->gblset) ecb->gblset (rtx, idx, val);
}
return 0;
}
HAWK_INLINE void hawk_rtx_setretval (hawk_rtx_t* rtx, hawk_val_t* val)
{
hawk_rtx_refdownval (rtx, RTX_STACK_RETVAL(rtx));
RTX_STACK_RETVAL(rtx) = val;
/* should use the same trick as run_return */
hawk_rtx_refupval (rtx, val);
}
HAWK_INLINE int hawk_rtx_setgbl (hawk_rtx_t* rtx, int id, hawk_val_t* val)
{
HAWK_ASSERT (id >= 0 && id < (int)HAWK_ARR_SIZE(rtx->awk->parse.gbls));
return set_global(rtx, id, HAWK_NULL, val, 0);
}
int hawk_rtx_setfilename (hawk_rtx_t* rtx, const hawk_ooch_t* name, hawk_oow_t len)
{
hawk_val_t* tmp;
int n;
tmp = hawk_rtx_makestrvalwithoochars(rtx, name, len);
if (tmp == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, tmp);
n = hawk_rtx_setgbl (rtx, HAWK_GBL_FILENAME, tmp);
hawk_rtx_refdownval (rtx, tmp);
return n;
}
int hawk_rtx_setofilename (hawk_rtx_t* rtx, const hawk_ooch_t* name, hawk_oow_t len)
{
hawk_val_t* tmp;
int n;
if (rtx->awk->opt.trait & HAWK_NEXTOFILE)
{
tmp = hawk_rtx_makestrvalwithoochars(rtx, name, len);
if (tmp == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, tmp);
n = hawk_rtx_setgbl (rtx, HAWK_GBL_OFILENAME, tmp);
hawk_rtx_refdownval (rtx, tmp);
}
else n = 0;
return n;
}
hawk_htb_t* hawk_rtx_getnvmap (hawk_rtx_t* rtx)
{
return rtx->named;
}
struct module_init_ctx_t
{
hawk_oow_t count;
hawk_rtx_t* rtx;
};
struct module_fini_ctx_t
{
hawk_oow_t limit;
hawk_oow_t count;
hawk_rtx_t* rtx;
};
static hawk_rbt_walk_t init_module (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair, void* ctx)
{
hawk_mod_data_t* md;
struct module_init_ctx_t* mic;
mic = (struct module_init_ctx_t*)ctx;
md = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair);
if (md->mod.init && md->mod.init(&md->mod, mic->rtx) <= -1)
return HAWK_RBT_WALK_STOP;
mic->count++;
return HAWK_RBT_WALK_FORWARD;
}
static hawk_rbt_walk_t fini_module (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair, void* ctx)
{
hawk_mod_data_t* md;
struct module_fini_ctx_t* mfc;
mfc = (struct module_fini_ctx_t*)ctx;
if (mfc->limit > 0 && mfc->count >= mfc->limit)
return HAWK_RBT_WALK_STOP;
md = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair);
if (md->mod.fini) md->mod.fini (&md->mod, mfc->rtx);
mfc->count++;
return HAWK_RBT_WALK_FORWARD;
}
hawk_rtx_t* hawk_rtx_open (hawk_t* awk, hawk_oow_t xtnsize, hawk_rio_cbs_t* rio)
{
hawk_rtx_t* rtx;
struct module_init_ctx_t mic;
/* clear the awk error code */
hawk_seterrnum (awk, HAWK_NULL, HAWK_ENOERR);
/* check if the code has ever been parsed */
if (awk->tree.ngbls == 0 &&
awk->tree.begin == HAWK_NULL &&
awk->tree.end == HAWK_NULL &&
awk->tree.chain_size == 0 &&
hawk_htb_getsize(awk->tree.funs) == 0)
{
hawk_seterrnum (awk, HAWK_NULL, HAWK_EPERM);
return HAWK_NULL;
}
/* allocate the storage for the rtx object */
rtx = (hawk_rtx_t*)hawk_allocmem(awk, HAWK_SIZEOF(hawk_rtx_t) + xtnsize);
if (!rtx)
{
/* if it fails, the failure is reported thru the awk object */
return HAWK_NULL;
}
/* initialize the rtx object */
HAWK_MEMSET (rtx, 0, HAWK_SIZEOF(hawk_rtx_t) + xtnsize);
rtx->_instsize = HAWK_SIZEOF(hawk_rtx_t);
if (init_rtx(rtx, awk, rio) <= -1)
{
hawk_freemem (awk, rtx);
return HAWK_NULL;
}
if (init_globals(rtx) <= -1)
{
hawk_rtx_errortohawk (rtx, awk);
fini_rtx (rtx, 0);
hawk_freemem (awk, rtx);
return HAWK_NULL;
}
mic.count = 0;
mic.rtx = rtx;
hawk_rbt_walk (rtx->awk->modtab, init_module, &mic);
if (mic.count != HAWK_RBT_SIZE(rtx->awk->modtab))
{
if (mic.count > 0)
{
struct module_fini_ctx_t mfc;
mfc.limit = mic.count;
mfc.count = 0;
hawk_rbt_walk (rtx->awk->modtab, fini_module, &mfc);
}
fini_rtx (rtx, 1);
hawk_freemem (awk, rtx);
return HAWK_NULL;
}
return rtx;
}
void hawk_rtx_close (hawk_rtx_t* rtx)
{
hawk_rtx_ecb_t* ecb;
struct module_fini_ctx_t mfc;
mfc.limit = 0;
mfc.count = 0;
mfc.rtx = rtx;
hawk_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);
hawk_freemem (hawk_rtx_gethawk(rtx), rtx);
}
void hawk_rtx_halt (hawk_rtx_t* rtx)
{
rtx->exit_level = EXIT_ABORT;
}
int hawk_rtx_ishalt (hawk_rtx_t* rtx)
{
return (rtx->exit_level == EXIT_ABORT || rtx->awk->haltall);
}
void hawk_rtx_getrio (hawk_rtx_t* rtx, hawk_rio_cbs_t* rio)
{
rio->pipe = rtx->rio.handler[HAWK_RIO_PIPE];
rio->file = rtx->rio.handler[HAWK_RIO_FILE];
rio->console = rtx->rio.handler[HAWK_RIO_CONSOLE];
}
void hawk_rtx_setrio (hawk_rtx_t* rtx, const hawk_rio_cbs_t* rio)
{
rtx->rio.handler[HAWK_RIO_PIPE] = rio->pipe;
rtx->rio.handler[HAWK_RIO_FILE] = rio->file;
rtx->rio.handler[HAWK_RIO_CONSOLE] = rio->console;
}
hawk_rtx_ecb_t* hawk_rtx_popecb (hawk_rtx_t* rtx)
{
hawk_rtx_ecb_t* top = rtx->ecb;
if (top) rtx->ecb = top->next;
return top;
}
void hawk_rtx_pushecb (hawk_rtx_t* rtx, hawk_rtx_ecb_t* ecb)
{
ecb->next = rtx->ecb;
rtx->ecb = ecb;
}
static void free_namedval (hawk_htb_t* map, void* dptr, hawk_oow_t dlen)
{
hawk_rtx_refdownval (*(hawk_rtx_t**)hawk_htb_getxtn(map), dptr);
}
static void same_namedval (hawk_htb_t* map, void* dptr, hawk_oow_t dlen)
{
hawk_rtx_refdownval_nofree (*(hawk_rtx_t**)hawk_htb_getxtn(map), dptr);
}
static int init_rtx (hawk_rtx_t* rtx, hawk_t* awk, hawk_rio_cbs_t* rio)
{
static hawk_htb_style_t style_for_named =
{
{
HAWK_HTB_COPIER_INLINE,
HAWK_HTB_COPIER_DEFAULT
},
{
HAWK_HTB_FREEER_DEFAULT,
free_namedval
},
HAWK_HTB_COMPER_DEFAULT,
same_namedval,
HAWK_HTB_SIZER_DEFAULT,
HAWK_HTB_HASHER_DEFAULT
};
hawk_oow_t stack_limit;
rtx->_gem = awk->_gem;
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 < HAWK_MIN_RTX_STACK_LIMIT) stack_limit = HAWK_MIN_RTX_STACK_LIMIT;
rtx->stack = hawk_rtx_allocmem(rtx, stack_limit * HAWK_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 = HAWK_NULL;
rtx->vmgr.ifree = HAWK_NULL;
rtx->vmgr.rchunk = HAWK_NULL;
rtx->vmgr.rfree = HAWK_NULL;
rtx->inrec.buf_pos = 0;
rtx->inrec.buf_len = 0;
rtx->inrec.flds = HAWK_NULL;
rtx->inrec.nflds = 0;
rtx->inrec.maxflds = 0;
rtx->inrec.d0 = hawk_val_nil;
if (hawk_ooecs_init(&rtx->inrec.line, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1) goto oops_1;
if (hawk_ooecs_init(&rtx->inrec.linew, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1) goto oops_2;
if (hawk_ooecs_init(&rtx->inrec.lineg, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1) goto oops_3;
if (hawk_becs_init(&rtx->inrec.linegb, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1) goto oops_4;
if (hawk_ooecs_init(&rtx->format.out, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_5;
if (hawk_ooecs_init(&rtx->format.fmt, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_6;
if (hawk_becs_init(&rtx->formatmbs.out, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_7;
if (hawk_becs_init(&rtx->formatmbs.fmt, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_8;
rtx->named = hawk_htb_open(hawk_rtx_getgem(rtx), HAWK_SIZEOF(rtx), 1024, 70, HAWK_SIZEOF(hawk_ooch_t), 1);
if (!rtx->named) goto oops_9;
*(hawk_rtx_t**)hawk_htb_getxtn(rtx->named) = rtx;
hawk_htb_setstyle (rtx->named, &style_for_named);
rtx->format.tmp.ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, 4096 * HAWK_SIZEOF(hawk_ooch_t));
if (!rtx->format.tmp.ptr) goto oops_10; /* 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 = (hawk_bch_t*)hawk_rtx_allocmem(rtx, 4096 * HAWK_SIZEOF(hawk_bch_t));
if (!rtx->formatmbs.tmp.ptr) goto oops_11;
rtx->formatmbs.tmp.len = 4096;
rtx->formatmbs.tmp.inc = 4096 * 2;
if (rtx->awk->tree.chain_size > 0)
{
rtx->pattern_range_state = (hawk_oob_t*)hawk_rtx_allocmem(rtx, rtx->awk->tree.chain_size * HAWK_SIZEOF(hawk_oob_t));
if (!rtx->pattern_range_state) goto oops_12;
HAWK_MEMSET (rtx->pattern_range_state, 0, rtx->awk->tree.chain_size * HAWK_SIZEOF(hawk_oob_t));
}
else rtx->pattern_range_state = HAWK_NULL;
if (rio)
{
rtx->rio.handler[HAWK_RIO_PIPE] = rio->pipe;
rtx->rio.handler[HAWK_RIO_FILE] = rio->file;
rtx->rio.handler[HAWK_RIO_CONSOLE] = rio->console;
rtx->rio.chain = HAWK_NULL;
}
rtx->gbl.brs[0] = HAWK_NULL;
rtx->gbl.brs[1] = HAWK_NULL;
rtx->gbl.rs[0] = HAWK_NULL;
rtx->gbl.rs[1] = HAWK_NULL;
rtx->gbl.fs[0] = HAWK_NULL;
rtx->gbl.fs[1] = HAWK_NULL;
rtx->gbl.ignorecase = 0;
rtx->gbl.striprecspc = -1;
return 0;
oops_12:
hawk_rtx_freemem (rtx, rtx->formatmbs.tmp.ptr);
oops_11:
hawk_rtx_freemem (rtx, rtx->format.tmp.ptr);
oops_10:
hawk_htb_close (rtx->named);
oops_9:
hawk_becs_fini (&rtx->formatmbs.fmt);
oops_8:
hawk_becs_fini (&rtx->formatmbs.out);
oops_7:
hawk_ooecs_fini (&rtx->format.fmt);
oops_6:
hawk_ooecs_fini (&rtx->format.out);
oops_5:
hawk_becs_fini (&rtx->inrec.linegb);
oops_4:
hawk_ooecs_fini (&rtx->inrec.lineg);
oops_3:
hawk_ooecs_fini (&rtx->inrec.linew);
oops_2:
hawk_ooecs_fini (&rtx->inrec.line);
oops_1:
hawk_rtx_freemem (rtx, rtx->stack);
oops_0:
return -1;
}
static void fini_rtx (hawk_rtx_t* rtx, int fini_globals)
{
if (rtx->pattern_range_state)
hawk_rtx_freemem (rtx, rtx->pattern_range_state);
/* close all pending io's */
/* TODO: what if this operation fails? */
hawk_rtx_cleario (rtx);
HAWK_ASSERT (rtx->rio.chain == HAWK_NULL);
if (rtx->gbl.brs[0])
{
hawk_rtx_freerex (rtx, rtx->gbl.brs[0], rtx->gbl.brs[1]);
rtx->gbl.brs[0] = HAWK_NULL;
rtx->gbl.brs[1] = HAWK_NULL;
}
if (rtx->gbl.rs[0])
{
hawk_rtx_freerex (rtx, rtx->gbl.rs[0], rtx->gbl.rs[1]);
rtx->gbl.rs[0] = HAWK_NULL;
rtx->gbl.rs[1] = HAWK_NULL;
}
if (rtx->gbl.fs[0])
{
hawk_rtx_freerex (rtx, rtx->gbl.fs[0], rtx->gbl.fs[1]);
rtx->gbl.fs[0] = HAWK_NULL;
rtx->gbl.fs[1] = HAWK_NULL;
}
if (rtx->gbl.convfmt.ptr != HAWK_NULL &&
rtx->gbl.convfmt.ptr != DEFAULT_CONVFMT)
{
hawk_rtx_freemem (rtx, rtx->gbl.convfmt.ptr);
rtx->gbl.convfmt.ptr = HAWK_NULL;
rtx->gbl.convfmt.len = 0;
}
if (rtx->gbl.ofmt.ptr != HAWK_NULL &&
rtx->gbl.ofmt.ptr != DEFAULT_OFMT)
{
hawk_rtx_freemem (rtx, rtx->gbl.ofmt.ptr);
rtx->gbl.ofmt.ptr = HAWK_NULL;
rtx->gbl.ofmt.len = 0;
}
if (rtx->gbl.ofs.ptr != HAWK_NULL &&
rtx->gbl.ofs.ptr != DEFAULT_OFS)
{
hawk_rtx_freemem (rtx, rtx->gbl.ofs.ptr);
rtx->gbl.ofs.ptr = HAWK_NULL;
rtx->gbl.ofs.len = 0;
}
if (rtx->gbl.ors.ptr != HAWK_NULL &&
rtx->gbl.ors.ptr != DEFAULT_ORS &&
rtx->gbl.ors.ptr != DEFAULT_ORS_CRLF)
{
hawk_rtx_freemem (rtx, rtx->gbl.ors.ptr);
rtx->gbl.ors.ptr = HAWK_NULL;
rtx->gbl.ors.len = 0;
}
if (rtx->gbl.subsep.ptr != HAWK_NULL &&
rtx->gbl.subsep.ptr != DEFAULT_SUBSEP)
{
hawk_rtx_freemem (rtx, rtx->gbl.subsep.ptr);
rtx->gbl.subsep.ptr = HAWK_NULL;
rtx->gbl.subsep.len = 0;
}
hawk_rtx_freemem (rtx, rtx->formatmbs.tmp.ptr);
rtx->formatmbs.tmp.ptr = HAWK_NULL;
rtx->formatmbs.tmp.len = 0;
hawk_becs_fini (&rtx->formatmbs.fmt);
hawk_becs_fini (&rtx->formatmbs.out);
hawk_rtx_freemem (rtx, rtx->format.tmp.ptr);
rtx->format.tmp.ptr = HAWK_NULL;
rtx->format.tmp.len = 0;
hawk_ooecs_fini (&rtx->format.fmt);
hawk_ooecs_fini (&rtx->format.out);
/* destroy input record. hawk_rtx_clrrec() should be called
* before the stack has been destroyed because it may try
* to change the value to HAWK_GBL_NF. */
hawk_rtx_clrrec (rtx, 0);
if (rtx->inrec.flds)
{
hawk_rtx_freemem (rtx, rtx->inrec.flds);
rtx->inrec.flds = HAWK_NULL;
rtx->inrec.maxflds = 0;
}
hawk_becs_fini (&rtx->inrec.linegb);
hawk_ooecs_fini (&rtx->inrec.lineg);
hawk_ooecs_fini (&rtx->inrec.linew);
hawk_ooecs_fini (&rtx->inrec.line);
if (fini_globals) refdown_globals (rtx, 1);
/* destroy the stack if necessary */
if (rtx->stack)
{
HAWK_ASSERT (rtx->stack_top == 0);
hawk_rtx_freemem (rtx, rtx->stack);
rtx->stack = HAWK_NULL;
rtx->stack_top = 0;
rtx->stack_base = 0;
rtx->stack_limit = 0;
}
/* destroy named variables */
hawk_htb_close (rtx->named);
/* destroy values in free list */
while (rtx->rcache_count > 0)
{
hawk_val_ref_t* tmp = rtx->rcache[--rtx->rcache_count];
hawk_rtx_freeval (rtx, (hawk_val_t*)tmp, 0);
}
#if defined(ENABLE_FEATURE_SCACHE)
{
int i;
for (i = 0; i < HAWK_COUNTOF(rtx->scache_count); i++)
{
while (rtx->scache_count[i] > 0)
{
hawk_val_str_t* t = rtx->scache[i][--rtx->scache_count[i]];
hawk_rtx_freeval (rtx, (hawk_val_t*)t, 0);
}
}
}
#endif
hawk_rtx_freevalchunk (rtx, rtx->vmgr.ichunk);
hawk_rtx_freevalchunk (rtx, rtx->vmgr.rchunk);
rtx->vmgr.ichunk = HAWK_NULL;
rtx->vmgr.rchunk = HAWK_NULL;
}
static int update_fnr (hawk_rtx_t* rtx, hawk_int_t fnr, hawk_int_t nr)
{
hawk_val_t* tmp1, * tmp2;
tmp1 = hawk_rtx_makeintval(rtx, fnr);
if (!tmp1) return -1;
hawk_rtx_refupval (rtx, tmp1);
if (nr == fnr) tmp2 = tmp1;
else
{
tmp2 = hawk_rtx_makeintval(rtx, nr);
if (!tmp2)
{
hawk_rtx_refdownval (rtx, tmp1);
return -1;
}
hawk_rtx_refupval (rtx, tmp2);
}
if (hawk_rtx_setgbl (rtx, HAWK_GBL_FNR, tmp1) == -1)
{
if (nr != fnr) hawk_rtx_refdownval (rtx, tmp2);
hawk_rtx_refdownval (rtx, tmp1);
return -1;
}
if (hawk_rtx_setgbl (rtx, HAWK_GBL_NR, tmp2) == -1)
{
if (nr != fnr) hawk_rtx_refdownval (rtx, tmp2);
hawk_rtx_refdownval (rtx, tmp1);
return -1;
}
if (nr != fnr) hawk_rtx_refdownval (rtx, tmp2);
hawk_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 (hawk_rtx_t* rtx)
{
hawk_oow_t saved_stack_top;
hawk_oow_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,hawk_val_nil) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_ENOMEM);
goto oops;
}
}
/* override NF to zero */
if (hawk_rtx_setgbl(rtx, HAWK_GBL_NF, HAWK_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 hawk_val_nils and HAWK_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 (hawk_rtx_t* rtx)
{
struct gtab_t
{
int idx;
const hawk_ooch_t* str[2];
};
static struct gtab_t gtab[7] =
{
{ HAWK_GBL_CONVFMT, { DEFAULT_CONVFMT, DEFAULT_CONVFMT } },
{ HAWK_GBL_FILENAME, { HAWK_NULL, HAWK_NULL } },
{ HAWK_GBL_OFILENAME, { HAWK_NULL, HAWK_NULL } },
{ HAWK_GBL_OFMT, { DEFAULT_OFMT, DEFAULT_OFMT } },
{ HAWK_GBL_OFS, { DEFAULT_OFS, DEFAULT_OFS } },
{ HAWK_GBL_ORS, { DEFAULT_ORS, DEFAULT_ORS_CRLF } },
{ HAWK_GBL_SUBSEP, { DEFAULT_SUBSEP, DEFAULT_SUBSEP } },
};
hawk_val_t* tmp;
hawk_oow_t i, j;
int stridx;
stridx = (rtx->awk->opt.trait & HAWK_CRLF)? 1: 0;
for (i = 0; i < HAWK_COUNTOF(gtab); i++)
{
if (gtab[i].str[stridx] == HAWK_NULL || gtab[i].str[stridx][0] == HAWK_T('\0'))
{
tmp = hawk_val_zls;
}
else
{
tmp = hawk_rtx_makestrvalwithoocstr (rtx, gtab[i].str[stridx]);
if (tmp == HAWK_NULL) return -1;
}
hawk_rtx_refupval (rtx, tmp);
HAWK_ASSERT (RTX_STACK_GBL(rtx,gtab[i].idx) == hawk_val_nil);
if (hawk_rtx_setgbl(rtx, gtab[i].idx, tmp) == -1)
{
for (j = 0; j < i; j++)
{
hawk_rtx_setgbl (rtx, gtab[i].idx, hawk_val_nil);
}
hawk_rtx_refdownval (rtx, tmp);
return -1;
}
hawk_rtx_refdownval (rtx, tmp);
}
return 0;
}
static void refdown_globals (hawk_rtx_t* run, int pop)
{
hawk_oow_t ngbls;
ngbls = run->awk->tree.ngbls;
while (ngbls > 0)
{
--ngbls;
hawk_rtx_refdownval (run, RTX_STACK_GBL(run,ngbls));
if (pop) __raw_pop (run);
else RTX_STACK_GBL(run,ngbls) = hawk_val_nil;
}
}
static int init_globals (hawk_rtx_t* rtx)
{
/* the stack must be clean when this function is invoked */
HAWK_ASSERT (rtx->stack_base == 0);
HAWK_ASSERT (rtx->stack_top == 0);
if (prepare_globals (rtx) == -1) return -1;
if (update_fnr (rtx, 0, 0) == -1) goto oops;
if (defaultify_globals (rtx) == -1) goto oops;
return 0;
oops:
refdown_globals (rtx, 1);
return -1;
}
struct capture_retval_data_t
{
hawk_rtx_t* rtx;
hawk_val_t* val;
};
static void capture_retval_on_exit (void* arg)
{
struct capture_retval_data_t* data;
data = (struct capture_retval_data_t*)arg;
data->val = RTX_STACK_RETVAL(data->rtx);
hawk_rtx_refupval (data->rtx, data->val);
}
static int enter_stack_frame (hawk_rtx_t* rtx)
{
hawk_oow_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,hawk_val_nil) <= -1) goto oops;
/* secure space for RTX_STACK_NARGS */
if (__raw_push(rtx,hawk_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;
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_ENOMEM);
return -1;
}
static void exit_stack_frame (hawk_rtx_t* run)
{
/* At this point, the current stack frame should have
* the 4 entries pushed in enter_stack_frame(). */
HAWK_ASSERT ((run->stack_top-run->stack_base) == 4);
run->stack_top = (hawk_oow_t)run->stack[run->stack_base + 1];
run->stack_base = (hawk_oow_t)run->stack[run->stack_base + 0];
}
static hawk_val_t* run_bpae_loop (hawk_rtx_t* rtx)
{
hawk_nde_t* nde;
hawk_oow_t nargs, i;
hawk_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 != HAWK_NULL && rtx->exit_level < EXIT_GLOBAL;
nde = nde->next)
{
hawk_nde_blk_t* blk;
blk = (hawk_nde_blk_t*)nde;
HAWK_ASSERT (blk->type == HAWK_NDE_BLK);
rtx->active_block = blk;
rtx->exit_level = EXIT_NONE;
if (run_block (rtx, blk) == -1) ret = -1;
}
if (ret <= -1 && hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)
{
/* an error is returned with no error number set.
* this trait is used by eval_expression() to
* abort the evaluation when exit() is executed
* during function evaluation */
ret = 0;
CLRERR (rtx); /* clear it just in case */
}
/* run pattern block loops */
if (ret == 0 &&
(rtx->awk->tree.chain != HAWK_NULL ||
rtx->awk->tree.end != HAWK_NULL) &&
rtx->exit_level < EXIT_GLOBAL)
{
if (run_pblocks(rtx) <= -1) ret = -1;
}
if (ret <= -1 && hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)
{
/* an error is returned with no error number set.
* this trait is used by eval_expression() to
* abort the evaluation when exit() is executed
* during function evaluation */
ret = 0;
CLRERR (rtx); /* clear it just in case */
}
/* execute END blocks. the first END block is executed if the
* program is not explicitly aborted with hawk_rtx_halt().*/
for (nde = rtx->awk->tree.end;
ret == 0 && nde != HAWK_NULL && rtx->exit_level < EXIT_ABORT;
nde = nde->next)
{
hawk_nde_blk_t* blk;
blk = (hawk_nde_blk_t*)nde;
HAWK_ASSERT (blk->type == HAWK_NDE_BLK);
rtx->active_block = blk;
rtx->exit_level = EXIT_NONE;
if (run_block (rtx, blk) <= -1) ret = -1;
else if (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 && hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)
{
/* an error is returned with no error number set.
* this trait is used by eval_expression() to
* abort the evaluation when exit() is executed
* during function evaluation */
ret = 0;
CLRERR (rtx); /* clear it just in case */
}
/* derefrence all arguments. however, there should be no arguments
* pushed to the stack as asserted below. we didn't push any arguments
* for BEGIN/pattern action/END block execution.*/
nargs = (hawk_oow_t)RTX_STACK_NARGS(rtx);
HAWK_ASSERT (nargs == 0);
for (i = 0; i < nargs; i++)
hawk_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 */
hawk_rtx_refdownval (rtx, retv);
retv = HAWK_NULL;
}
return retv;
}
/* start the BEGIN-pattern block-END loop */
hawk_val_t* hawk_rtx_loop (hawk_rtx_t* rtx)
{
hawk_val_t* retv = HAWK_NULL;
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;
}
hawk_val_t* hawk_rtx_execwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* args[], hawk_oow_t nargs)
{
return (rtx->awk->parse.pragma.startup[0] != '\0')?
hawk_rtx_callwithooucstrarr(rtx, rtx->awk->parse.pragma.startup, args, nargs):
hawk_rtx_loop(rtx);
}
hawk_val_t* hawk_rtx_execwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* args[], hawk_oow_t nargs)
{
return (rtx->awk->parse.pragma.startup[0] != '\0')?
hawk_rtx_callwithoobcstrarr(rtx, rtx->awk->parse.pragma.startup, args, nargs):
hawk_rtx_loop(rtx);
}
/* find an AWK function by name */
static hawk_fun_t* find_fun (hawk_rtx_t* rtx, const hawk_ooch_t* name)
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->awk->tree.funs, name, hawk_count_oocstr(name));
if (!pair)
{
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_EFUNNF, HAWK_T("unable to find function '%js'"), name);
return HAWK_NULL;
}
return (hawk_fun_t*)HAWK_HTB_VPTR(pair);
}
hawk_fun_t* hawk_rtx_findfunwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* name)
{
#if defined(HAWK_OOCH_IS_BCH)
return find_fun(rtx, name);
#else
hawk_ucs_t wcs;
hawk_fun_t* fun;
wcs.ptr = hawk_rtx_dupbtoucstr(rtx, name, &wcs.len, 0);
if (!wcs.ptr) return HAWK_NULL;
fun = find_fun(rtx, wcs.ptr);
hawk_rtx_freemem (rtx, wcs.ptr);
return fun;
#endif
}
hawk_fun_t* hawk_rtx_findfunwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* name)
{
#if defined(HAWK_OOCH_IS_BCH)
hawk_bcs_t mbs;
hawk_fun_t* fun;
mbs.ptr = hawk_rtx_duputobcstr(rtx, name, &mbs.len);
if (!mbs.ptr) return HAWK_NULL;
fun = find_fun(rtx, mbs.ptr);
hawk_rtx_freemem (rtx, mbs.ptr);
return fun;
#else
return find_fun(rtx, name);
#endif
}
/* call an AWK function by the function structure */
hawk_val_t* hawk_rtx_callfun (hawk_rtx_t* rtx, hawk_fun_t* fun, hawk_val_t* args[], hawk_oow_t nargs)
{
struct capture_retval_data_t crdata;
hawk_val_t* v;
struct pafv_t pafv/*= { args, nargs }*/;
hawk_nde_fncall_t call;
HAWK_ASSERT (fun != HAWK_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 hawk_rtx_halt() is invoked */
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EPERM);
return HAWK_NULL;
}
/*rtx->exit_level = EXIT_NONE;*/
#if 0
if (fun->argspec)
{
/* this function contains pass-by-reference parameters.
* i don't support the call here as it requires variables */
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_EPERM, HAWK_T("not allowed to call '%.*js' with pass-by-reference parameters"), fun->name.len, fun->name.ptr);
return HAWK_NULL;
}
#endif
/* forge a fake node containing a function call */
HAWK_MEMSET (&call, 0, HAWK_SIZEOF(call));
call.type = HAWK_NDE_FNCALL_FUN;
call.u.fun.name = fun->name;
call.nargs = nargs;
/* keep HAWK_NULL in call.args so that __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? */
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EARGTM);
return HAWK_NULL;
}
/* now that the function is found and ok, let's execute it */
crdata.rtx = rtx;
crdata.val = HAWK_NULL;
v = __eval_call(rtx, (hawk_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. */
hawk_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 */
hawk_val_t* hawk_rtx_callwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_val_t* args[], hawk_oow_t nargs)
{
hawk_fun_t* fun;
fun = hawk_rtx_findfunwithucstr(rtx, name);
if (!fun) return HAWK_NULL;
return hawk_rtx_callfun(rtx, fun, args, nargs);
}
hawk_val_t* hawk_rtx_callwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_val_t* args[], hawk_oow_t nargs)
{
hawk_fun_t* fun;
fun = hawk_rtx_findfunwithbcstr(rtx, name);
if (!fun) return HAWK_NULL;
return hawk_rtx_callfun(rtx, fun, args, nargs);
}
hawk_val_t* hawk_rtx_callwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* name, const hawk_uch_t* args[], hawk_oow_t nargs)
{
hawk_oow_t i;
hawk_val_t** v, * ret;
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
if (!v) return HAWK_NULL;
for (i = 0; i < nargs; i++)
{
v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]);
if (!v[i])
{
ret = HAWK_NULL;
goto oops;
}
hawk_rtx_refupval (rtx, v[i]);
}
ret = hawk_rtx_callwithucstr(rtx, name, v, nargs);
oops:
while (i > 0)
{
hawk_rtx_refdownval (rtx, v[--i]);
}
hawk_rtx_freemem (rtx, v);
return ret;
}
hawk_val_t* hawk_rtx_callwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* name, const hawk_bch_t* args[], hawk_oow_t nargs)
{
hawk_oow_t i;
hawk_val_t** v, * ret;
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
if (!v) return HAWK_NULL;
for (i = 0; i < nargs; i++)
{
v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]);
if (!v[i])
{
ret = HAWK_NULL;
goto oops;
}
hawk_rtx_refupval (rtx, v[i]);
}
ret = hawk_rtx_callwithbcstr(rtx, name, v, nargs);
oops:
while (i > 0)
{
hawk_rtx_refdownval (rtx, v[--i]);
}
hawk_rtx_freemem (rtx, v);
return ret;
}
hawk_val_t* hawk_rtx_callwithooucstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* name, const hawk_uch_t* args[], hawk_oow_t nargs)
{
hawk_oow_t i;
hawk_val_t** v, * ret;
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
if (!v) return HAWK_NULL;
for (i = 0; i < nargs; i++)
{
v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]);
if (!v[i])
{
ret = HAWK_NULL;
goto oops;
}
hawk_rtx_refupval (rtx, v[i]);
}
ret = hawk_rtx_callwithoocstr(rtx, name, v, nargs);
oops:
while (i > 0)
{
hawk_rtx_refdownval (rtx, v[--i]);
}
hawk_rtx_freemem (rtx, v);
return ret;
}
hawk_val_t* hawk_rtx_callwithoobcstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* name, const hawk_bch_t* args[], hawk_oow_t nargs)
{
hawk_oow_t i;
hawk_val_t** v, * ret;
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
if (!v) return HAWK_NULL;
for (i = 0; i < nargs; i++)
{
v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]);
if (!v[i])
{
ret = HAWK_NULL;
goto oops;
}
hawk_rtx_refupval (rtx, v[i]);
}
ret = hawk_rtx_callwithoocstr(rtx, name, v, nargs);
oops:
while (i > 0)
{
hawk_rtx_refdownval (rtx, v[--i]);
}
hawk_rtx_freemem (rtx, v);
return ret;
}
static int run_pblocks (hawk_rtx_t* rtx)
{
int n;
#define ADJUST_ERROR(run) \
if (rtx->awk->tree.chain != HAWK_NULL) \
{ \
if (rtx->awk->tree.chain->pattern != HAWK_NULL) \
ADJERR_LOC (rtx, &rtx->awk->tree.chain->pattern->loc); \
else if (rtx->awk->tree.chain->action != HAWK_NULL) \
ADJERR_LOC (rtx, &rtx->awk->tree.chain->action->loc); \
} \
else if (rtx->awk->tree.end != HAWK_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 (hawk_rtx_t* rtx, hawk_chain_t* chain)
{
hawk_oow_t bno = 0;
while (rtx->exit_level < EXIT_GLOBAL && chain)
{
if (rtx->exit_level == EXIT_NEXT)
{
rtx->exit_level = EXIT_NONE;
break;
}
if (run_pblock(rtx, chain, bno) <= -1) return -1;
chain = chain->next;
bno++;
}
return 0;
}
static int run_pblock (hawk_rtx_t* rtx, hawk_chain_t* cha, hawk_oow_t bno)
{
hawk_nde_t* ptn;
hawk_nde_blk_t* blk;
ptn = cha->pattern;
blk = (hawk_nde_blk_t*)cha->action;
if (!ptn)
{
/* just execute the block */
rtx->active_block = blk;
if (run_block(rtx, blk) <= -1) return -1;
}
else
{
if (!ptn->next)
{
/* pattern { ... } */
hawk_val_t* v1;
v1 = eval_expression(rtx, ptn);
if (!v1) return -1;
hawk_rtx_refupval (rtx, v1);
if (hawk_rtx_valtobool(rtx, v1))
{
rtx->active_block = blk;
if (run_block(rtx, blk) <= -1)
{
hawk_rtx_refdownval (rtx, v1);
return -1;
}
}
hawk_rtx_refdownval (rtx, v1);
}
else
{
/* pattern, pattern { ... } */
HAWK_ASSERT (ptn->next->next == HAWK_NULL);
HAWK_ASSERT (rtx->pattern_range_state != HAWK_NULL);
if (rtx->pattern_range_state[bno] == 0)
{
hawk_val_t* v1;
v1 = eval_expression(rtx, ptn);
if (!v1) return -1;
hawk_rtx_refupval (rtx, v1);
if (hawk_rtx_valtobool(rtx, v1))
{
rtx->active_block = blk;
if (run_block(rtx, blk) <= -1)
{
hawk_rtx_refdownval (rtx, v1);
return -1;
}
rtx->pattern_range_state[bno] = 1;
}
hawk_rtx_refdownval (rtx, v1);
}
else if (rtx->pattern_range_state[bno] == 1)
{
hawk_val_t* v2;
v2 = eval_expression(rtx, ptn->next);
if (!v2) return -1;
hawk_rtx_refupval (rtx, v2);
rtx->active_block = blk;
if (run_block(rtx, blk) <= -1)
{
hawk_rtx_refdownval(rtx, v2);
return -1;
}
if (hawk_rtx_valtobool(rtx, v2)) rtx->pattern_range_state[bno] = 0;
hawk_rtx_refdownval (rtx, v2);
}
}
}
return 0;
}
static HAWK_INLINE int run_block0 (hawk_rtx_t* rtx, hawk_nde_blk_t* nde)
{
hawk_nde_t* p;
hawk_oow_t nlcls;
hawk_oow_t saved_stack_top;
int n = 0;
if (nde == HAWK_NULL)
{
/* blockless pattern - execute print $0*/
hawk_rtx_refupval (rtx, rtx->inrec.d0);
n = hawk_rtx_writeiostr(rtx, HAWK_OUT_CONSOLE, HAWK_T(""), HAWK_OOECS_PTR(&rtx->inrec.line), HAWK_OOECS_LEN(&rtx->inrec.line));
if (n <= -1)
{
hawk_rtx_refdownval (rtx, rtx->inrec.d0);
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
n = hawk_rtx_writeiostr(rtx, HAWK_OUT_CONSOLE, HAWK_T(""), rtx->gbl.ors.ptr, rtx->gbl.ors.len);
if (n <= -1)
{
hawk_rtx_refdownval (rtx, rtx->inrec.d0);
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
hawk_rtx_refdownval (rtx, rtx->inrec.d0);
return 0;
}
HAWK_ASSERT (nde->type == HAWK_NDE_BLK);
p = nde->body;
nlcls = nde->nlcls;
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx),
HAWK_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,hawk_val_nil) <= -1)
{
/* restore stack top */
rtx->stack_top = saved_stack_top;
return -1;
}
/* refupval is not required for hawk_val_nil */
}
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("executing block statements\n"));
#endif
while (p != HAWK_NULL && rtx->exit_level == EXIT_NONE)
{
if (run_statement(rtx, p) <= -1)
{
n = -1;
break;
}
p = p->next;
}
/* pop off local variables */
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("popping off local variables\n"));
#endif
nlcls = nde->nlcls;
while (nlcls > 0)
{
--nlcls;
hawk_rtx_refdownval (rtx, RTX_STACK_LCL(rtx,nlcls));
__raw_pop (rtx);
}
return n;
}
static int run_block (hawk_rtx_t* rtx, hawk_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)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EBLKNST);
return -1;;
}
rtx->depth.block++;
n = run_block0(rtx, nde);
rtx->depth.block--;
return n;
}
#define ON_STATEMENT(rtx,nde) do { \
hawk_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); \
} while(0)
static int run_statement (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
int xret;
hawk_val_t* tmp;
ON_STATEMENT (rtx, nde);
switch (nde->type)
{
case HAWK_NDE_NULL:
/* do nothing */
xret = 0;
break;
case HAWK_NDE_BLK:
xret = run_block(rtx, (hawk_nde_blk_t*)nde);
break;
case HAWK_NDE_IF:
xret = run_if(rtx, (hawk_nde_if_t*)nde);
break;
case HAWK_NDE_WHILE:
case HAWK_NDE_DOWHILE:
xret = run_while(rtx, (hawk_nde_while_t*)nde);
break;
case HAWK_NDE_FOR:
xret = run_for(rtx, (hawk_nde_for_t*)nde);
break;
case HAWK_NDE_FOREACH:
xret = run_foreach(rtx, (hawk_nde_foreach_t*)nde);
break;
case HAWK_NDE_BREAK:
xret = run_break(rtx, (hawk_nde_break_t*)nde);
break;
case HAWK_NDE_CONTINUE:
xret = run_continue(rtx, (hawk_nde_continue_t*)nde);
break;
case HAWK_NDE_RETURN:
xret = run_return(rtx, (hawk_nde_return_t*)nde);
break;
case HAWK_NDE_EXIT:
xret = run_exit(rtx, (hawk_nde_exit_t*)nde);
break;
case HAWK_NDE_NEXT:
xret = run_next(rtx, (hawk_nde_next_t*)nde);
break;
case HAWK_NDE_NEXTFILE:
xret = run_nextfile(rtx, (hawk_nde_nextfile_t*)nde);
break;
case HAWK_NDE_DELETE:
xret = run_delete(rtx, (hawk_nde_delete_t*)nde);
break;
case HAWK_NDE_RESET:
xret = run_reset(rtx, (hawk_nde_reset_t*)nde);
break;
case HAWK_NDE_PRINT:
if (rtx->awk->opt.trait & HAWK_TOLERANT) goto __fallback__;
xret = run_print(rtx, (hawk_nde_print_t*)nde);
break;
case HAWK_NDE_PRINTF:
if (rtx->awk->opt.trait & HAWK_TOLERANT) goto __fallback__;
xret = run_printf(rtx, (hawk_nde_print_t*)nde);
break;
default:
__fallback__:
tmp = eval_expression(rtx, nde);
if (tmp == HAWK_NULL) xret = -1;
else
{
/* destroy the value if not referenced */
hawk_rtx_refupval (rtx, tmp);
hawk_rtx_refdownval (rtx, tmp);
xret = 0;
}
break;
}
return xret;
}
static int run_if (hawk_rtx_t* rtx, hawk_nde_if_t* nde)
{
hawk_val_t* test;
int n = 0;
/* the test expression for the if statement cannot have
* chained expressions. this should not be allowed by the
* parser first of all */
HAWK_ASSERT (nde->test->next == HAWK_NULL);
test = eval_expression (rtx, nde->test);
if (test == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, test);
if (hawk_rtx_valtobool(rtx, test))
{
n = run_statement(rtx, nde->then_part);
}
else if (nde->else_part)
{
n = run_statement(rtx, nde->else_part);
}
hawk_rtx_refdownval (rtx, test); /* TODO: is this correct?*/
return n;
}
static int run_while (hawk_rtx_t* rtx, hawk_nde_while_t* nde)
{
hawk_val_t* test;
if (nde->type == HAWK_NDE_WHILE)
{
/* no chained expressions are allowed for the test
* expression of the while statement */
HAWK_ASSERT (nde->test->next == HAWK_NULL);
while (1)
{
ON_STATEMENT (rtx, nde->test);
test = eval_expression(rtx, nde->test);
if (!test) return -1;
hawk_rtx_refupval (rtx, test);
if (hawk_rtx_valtobool(rtx, test))
{
if (run_statement(rtx,nde->body) <= -1)
{
hawk_rtx_refdownval (rtx, test);
return -1;
}
}
else
{
hawk_rtx_refdownval (rtx, test);
break;
}
hawk_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 == HAWK_NDE_DOWHILE)
{
/* no chained expressions are allowed for the test
* expression of the while statement */
HAWK_ASSERT (nde->test->next == HAWK_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;
hawk_rtx_refupval (rtx, test);
if (!hawk_rtx_valtobool(rtx, test))
{
hawk_rtx_refdownval (rtx, test);
break;
}
hawk_rtx_refdownval (rtx, test);
}
while (1);
}
return 0;
}
static int run_for (hawk_rtx_t* rtx, hawk_nde_for_t* nde)
{
hawk_val_t* val;
if (nde->init != HAWK_NULL)
{
HAWK_ASSERT (nde->init->next == HAWK_NULL);
ON_STATEMENT (rtx, nde->init);
val = eval_expression(rtx,nde->init);
if (val == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, val);
hawk_rtx_refdownval (rtx, val);
}
while (1)
{
if (nde->test != HAWK_NULL)
{
hawk_val_t* test;
/* no chained expressions for the test expression of
* the for statement are allowed */
HAWK_ASSERT (nde->test->next == HAWK_NULL);
ON_STATEMENT (rtx, nde->test);
test = eval_expression (rtx, nde->test);
if (test == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, test);
if (hawk_rtx_valtobool(rtx, test))
{
if (run_statement(rtx,nde->body) == -1)
{
hawk_rtx_refdownval (rtx, test);
return -1;
}
}
else
{
hawk_rtx_refdownval (rtx, test);
break;
}
hawk_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 != HAWK_NULL)
{
HAWK_ASSERT (nde->incr->next == HAWK_NULL);
ON_STATEMENT (rtx, nde->incr);
val = eval_expression (rtx, nde->incr);
if (val == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, val);
hawk_rtx_refdownval (rtx, val);
}
}
return 0;
}
struct foreach_walker_t
{
hawk_rtx_t* rtx;
hawk_nde_t* var;
hawk_nde_t* body;
int ret;
};
static hawk_htb_walk_t walk_foreach (hawk_htb_t* map, hawk_htb_pair_t* pair, void* arg)
{
struct foreach_walker_t* w = (struct foreach_walker_t*)arg;
hawk_val_t* str;
str = (hawk_val_t*)hawk_rtx_makestrvalwithoochars(w->rtx, HAWK_HTB_KPTR(pair), HAWK_HTB_KLEN(pair));
if (str == HAWK_NULL)
{
ADJERR_LOC (w->rtx, &w->var->loc);
w->ret = -1;
return HAWK_HTB_WALK_STOP;
}
hawk_rtx_refupval (w->rtx, str);
if (do_assignment(w->rtx, w->var, str) == HAWK_NULL)
{
hawk_rtx_refdownval (w->rtx, str);
w->ret = -1;
return HAWK_HTB_WALK_STOP;
}
if (run_statement(w->rtx, w->body) == -1)
{
hawk_rtx_refdownval (w->rtx, str);
w->ret = -1;
return HAWK_HTB_WALK_STOP;
}
hawk_rtx_refdownval (w->rtx, str);
if (w->rtx->exit_level == EXIT_BREAK)
{
w->rtx->exit_level = EXIT_NONE;
return HAWK_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 HAWK_HTB_WALK_STOP;
}
return HAWK_HTB_WALK_FORWARD;
}
static int run_foreach (hawk_rtx_t* rtx, hawk_nde_foreach_t* nde)
{
hawk_nde_exp_t* test;
hawk_val_t* rv;
hawk_htb_t* map;
struct foreach_walker_t walker;
hawk_val_type_t rvtype;
test = (hawk_nde_exp_t*)nde->test;
HAWK_ASSERT (test->type == HAWK_NDE_EXP_BIN && test->opcode == HAWK_BINOP_IN);
/* chained expressions should not be allowed
* by the parser first of all */
HAWK_ASSERT (test->right->next == HAWK_NULL);
rv = eval_expression(rtx, test->right);
if (rv == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, rv);
rvtype = HAWK_RTX_GETVALTYPE(rtx, rv);
if (rvtype == HAWK_VAL_NIL)
{
/* just return without excuting the loop body */
hawk_rtx_refdownval (rtx, rv);
return 0;
}
else if (rvtype != HAWK_VAL_MAP)
{
hawk_rtx_refdownval (rtx, rv);
hawk_rtx_seterrnum (rtx, &test->right->loc, HAWK_ENOTMAPIN);
return -1;
}
map = ((hawk_val_map_t*)rv)->map;
walker.rtx = rtx;
walker.var = test->left;
walker.body = nde->body;
walker.ret = 0;
hawk_htb_walk (map, walk_foreach, &walker);
hawk_rtx_refdownval (rtx, rv);
return walker.ret;
}
static int run_break (hawk_rtx_t* run, hawk_nde_break_t* nde)
{
run->exit_level = EXIT_BREAK;
return 0;
}
static int run_continue (hawk_rtx_t* rtx, hawk_nde_continue_t* nde)
{
rtx->exit_level = EXIT_CONTINUE;
return 0;
}
static int run_return (hawk_rtx_t* rtx, hawk_nde_return_t* nde)
{
if (nde->val != HAWK_NULL)
{
hawk_val_t* val;
/* chained expressions should not be allowed
* by the parser first of all */
HAWK_ASSERT (nde->val->next == HAWK_NULL);
val = eval_expression (rtx, nde->val);
if (val == HAWK_NULL) return -1;
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP))
{
if (HAWK_RTX_GETVALTYPE (rtx, val) == HAWK_VAL_MAP)
{
/* cannot return a map */
hawk_rtx_refupval (rtx, val);
hawk_rtx_refdownval (rtx, val);
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EMAPRET);
return -1;
}
}
hawk_rtx_refdownval (rtx, RTX_STACK_RETVAL(rtx));
RTX_STACK_RETVAL(rtx) = val;
/* NOTE: see eval_call() for the trick */
hawk_rtx_refupval (rtx, val);
}
rtx->exit_level = EXIT_FUNCTION;
return 0;
}
static int run_exit (hawk_rtx_t* rtx, hawk_nde_exit_t* nde)
{
if (nde->val != HAWK_NULL)
{
hawk_val_t* val;
/* chained expressions should not be allowed
* by the parser first of all */
HAWK_ASSERT (nde->val->next == HAWK_NULL);
val = eval_expression (rtx, nde->val);
if (val == HAWK_NULL) return -1;
hawk_rtx_refdownval (rtx, RTX_STACK_RETVAL_GBL(rtx));
RTX_STACK_RETVAL_GBL(rtx) = val; /* global return value */
hawk_rtx_refupval (rtx, val);
}
rtx->exit_level = (nde->abort)? EXIT_ABORT: EXIT_GLOBAL;
return 0;
}
static int run_next (hawk_rtx_t* rtx, hawk_nde_next_t* nde)
{
/* the parser checks if next has been called in the begin/end
* block or whereever inappropriate. so the rtxtime doesn't
* check that explicitly */
if (rtx->active_block == (hawk_nde_blk_t*)rtx->awk->tree.begin)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ERNEXTBEG);
return -1;
}
else if (rtx->active_block == (hawk_nde_blk_t*)rtx->awk->tree.end)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ERNEXTEND);
return -1;
}
rtx->exit_level = EXIT_NEXT;
return 0;
}
static int run_nextinfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
{
int n;
/* normal nextfile statement */
if (rtx->active_block == (hawk_nde_blk_t*)rtx->awk->tree.begin)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ERNEXTFBEG);
return -1;
}
else if (rtx->active_block == (hawk_nde_blk_t*)rtx->awk->tree.end)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ERNEXTFEND);
return -1;
}
n = hawk_rtx_nextio_read (rtx, HAWK_IN_CONSOLE, HAWK_T(""));
if (n == -1)
{
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
if (n == 0)
{
/* no more input console */
rtx->exit_level = EXIT_GLOBAL;
return 0;
}
/* FNR resets to 0, NR remains the same */
if (update_fnr (rtx, 0, rtx->gbl.nr) == -1)
{
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
rtx->exit_level = EXIT_NEXT;
return 0;
}
static int run_nextoutfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
{
int n;
/* nextofile can be called from BEGIN and END block unlike nextfile */
n = hawk_rtx_nextio_write (rtx, HAWK_OUT_CONSOLE, HAWK_T(""));
if (n == -1)
{
/* adjust the error line */
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
if (n == 0)
{
/* should it terminate the program there is no more
* output console? no. there will just be no more console
* output */
/*rtx->exit_level = EXIT_GLOBAL;*/
return 0;
}
return 0;
}
static int run_nextfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
{
return (nde->out)?
run_nextoutfile (rtx, nde):
run_nextinfile (rtx, nde);
}
static int delete_indexed (
hawk_rtx_t* rtx, hawk_htb_t* map, hawk_nde_var_t* var)
{
hawk_val_t* idx;
HAWK_ASSERT (var->idx != HAWK_NULL);
idx = eval_expression (rtx, var->idx);
if (idx == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, idx);
if (HAWK_RTX_GETVALTYPE(rtx, idx) == HAWK_VAL_STR)
{
/* delete x["abc"] */
hawk_htb_delete (map, ((hawk_val_str_t*)idx)->val.ptr, ((hawk_val_str_t*)idx)->val.len);
hawk_rtx_refdownval (rtx, idx);
}
else
{
/* delete x[20] */
hawk_ooch_t buf[IDXBUFSIZE];
hawk_rtx_valtostr_out_t out;
/* try with a fixed-size buffer */
out.type = HAWK_RTX_VALTOSTR_CPLCPY;
out.u.cplcpy.ptr = buf;
out.u.cplcpy.len = HAWK_COUNTOF(buf);
if (hawk_rtx_valtostr(rtx, idx, &out) <= -1)
{
int n;
/* retry it in dynamic mode */
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
n = hawk_rtx_valtostr(rtx, idx, &out);
hawk_rtx_refdownval (rtx, idx);
if (n <= -1)
{
/* change the error line */
ADJERR_LOC (rtx, &var->loc);
return -1;
}
else
{
hawk_htb_delete (map, out.u.cpldup.ptr, out.u.cpldup.len);
hawk_rtx_freemem (rtx, out.u.cpldup.ptr);
}
}
else
{
hawk_rtx_refdownval (rtx, idx);
hawk_htb_delete (map, out.u.cplcpy.ptr, out.u.cplcpy.len);
}
}
return 0;
}
static int run_delete_named (hawk_rtx_t* rtx, hawk_nde_var_t* var)
{
hawk_htb_pair_t* pair;
/* if a named variable has an index part and a named indexed variable
* doesn't have an index part, the program is definitely wrong */
HAWK_ASSERT (
(var->type == HAWK_NDE_NAMED && var->idx == HAWK_NULL) ||
(var->type == HAWK_NDE_NAMEDIDX && var->idx != HAWK_NULL));
pair = hawk_htb_search(rtx->named, var->id.name.ptr, var->id.name.len);
if (pair == HAWK_NULL)
{
hawk_val_t* tmp;
/* value not set for the named variable.
* create a map and assign it to the variable */
tmp = hawk_rtx_makemapval(rtx);
if (tmp == HAWK_NULL)
{
/* adjust error line */
ADJERR_LOC (rtx, &var->loc);
return -1;
}
pair = hawk_htb_upsert(rtx->named, var->id.name.ptr, var->id.name.len, tmp, 0);
if (pair == HAWK_NULL)
{
hawk_rtx_refupval (rtx, tmp);
hawk_rtx_refdownval (rtx, tmp);
ADJERR_LOC (rtx, &var->loc);
return -1;
}
/* as this is the assignment, it needs to update
* the reference count of the target value. */
hawk_rtx_refupval (rtx, tmp);
}
else
{
hawk_val_t* val;
hawk_htb_t* map;
val = (hawk_val_t*)HAWK_HTB_VPTR(pair);
HAWK_ASSERT (val != HAWK_NULL);
if (HAWK_RTX_GETVALTYPE (rtx, val) != HAWK_VAL_MAP)
{
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_ENOTDEL, HAWK_T("'%.*js' not deletable"), var->id.name.len, var->id.name.ptr);
return -1;
}
map = ((hawk_val_map_t*)val)->map;
if (var->type == HAWK_NDE_NAMEDIDX)
{
if (delete_indexed(rtx, map, var) <= -1) return -1;
}
else
{
hawk_htb_clear (map);
}
}
return 0;
}
static int run_delete_unnamed (hawk_rtx_t* rtx, hawk_nde_var_t* var)
{
hawk_val_t* val;
hawk_val_type_t vtype;
switch (var->type)
{
case HAWK_NDE_GBL:
case HAWK_NDE_GBLIDX:
val = RTX_STACK_GBL(rtx,var->id.idxa);
break;
case HAWK_NDE_LCL:
case HAWK_NDE_LCLIDX:
val = RTX_STACK_LCL(rtx,var->id.idxa);
break;
default:
val = RTX_STACK_ARG(rtx,var->id.idxa);
break;
}
HAWK_ASSERT (val != HAWK_NULL);
vtype = HAWK_RTX_GETVALTYPE(rtx, val);
if (vtype == HAWK_VAL_NIL)
{
hawk_val_t* tmp;
/* value not set.
* create a map and assign it to the variable */
tmp = hawk_rtx_makemapval(rtx);
if (tmp == HAWK_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 HAWK_NDE_GBL:
case HAWK_NDE_GBLIDX:
{
int x;
hawk_rtx_refupval (rtx, tmp);
x = hawk_rtx_setgbl(rtx, (int)var->id.idxa, tmp);
hawk_rtx_refdownval (rtx, tmp);
if (x <= -1)
{
ADJERR_LOC (rtx, &var->loc);
return -1;
}
break;
}
case HAWK_NDE_LCL:
case HAWK_NDE_LCLIDX:
RTX_STACK_LCL(rtx,var->id.idxa) = tmp;
hawk_rtx_refupval (rtx, tmp);
break;
default:
RTX_STACK_ARG(rtx,var->id.idxa) = tmp;
hawk_rtx_refupval (rtx, tmp);
break;
}
}
else
{
hawk_htb_t* map;
if (vtype != HAWK_VAL_MAP)
{
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_ENOTDEL, HAWK_T("'%.*js' not deletable"), var->id.name.len, var->id.name.ptr);
return -1;
}
map = ((hawk_val_map_t*)val)->map;
if (var->type == HAWK_NDE_GBLIDX ||
var->type == HAWK_NDE_LCLIDX ||
var->type == HAWK_NDE_ARGIDX)
{
if (delete_indexed(rtx, map, var) <= -1) return -1;
}
else
{
hawk_htb_clear (map);
}
}
return 0;
}
static int run_delete (hawk_rtx_t* rtx, hawk_nde_delete_t* nde)
{
hawk_nde_var_t* var;
var = (hawk_nde_var_t*)nde->var;
switch (var->type)
{
case HAWK_NDE_NAMED:
case HAWK_NDE_NAMEDIDX:
return run_delete_named(rtx, var);
case HAWK_NDE_GBL:
case HAWK_NDE_LCL:
case HAWK_NDE_ARG:
case HAWK_NDE_GBLIDX:
case HAWK_NDE_LCLIDX:
case HAWK_NDE_ARGIDX:
return run_delete_unnamed(rtx, var);
default:
/* the delete statement cannot be called with other nodes than
* the variables such as a named variable, a named indexed variable, etc */
HAWK_ASSERT (!"should never happen - wrong target for delete");
hawk_rtx_seterrnum (rtx, &var->loc, HAWK_EBADARG);
return -1;
}
}
static int reset_variable (hawk_rtx_t* rtx, hawk_nde_var_t* var)
{
switch (var->type)
{
case HAWK_NDE_NAMED:
/* if a named variable has an index part, something is definitely wrong */
HAWK_ASSERT (var->type == HAWK_NDE_NAMED && var->idx == HAWK_NULL);
/* a named variable can be reset if removed from a internal map
to manage it */
hawk_htb_delete (rtx->named, var->id.name.ptr, var->id.name.len);
return 0;
case HAWK_NDE_GBL:
case HAWK_NDE_LCL:
case HAWK_NDE_ARG:
{
hawk_val_t* val;
switch (var->type)
{
case HAWK_NDE_GBL:
val = RTX_STACK_GBL(rtx,var->id.idxa);
break;
case HAWK_NDE_LCL:
val = RTX_STACK_LCL(rtx,var->id.idxa);
break;
case HAWK_NDE_ARG:
val = RTX_STACK_ARG(rtx,var->id.idxa);
break;
}
HAWK_ASSERT (val != HAWK_NULL);
if (HAWK_RTX_GETVALTYPE (rtx, val) != HAWK_VAL_NIL)
{
hawk_rtx_refdownval (rtx, val);
switch (var->type)
{
case HAWK_NDE_GBL:
RTX_STACK_GBL(rtx,var->id.idxa) = hawk_val_nil;
break;
case HAWK_NDE_LCL:
RTX_STACK_LCL(rtx,var->id.idxa) = hawk_val_nil;
break;
case HAWK_NDE_ARG:
RTX_STACK_ARG(rtx,var->id.idxa) = hawk_val_nil;
break;
}
}
return 0;
}
default:
/* the reset statement can only be called with plain variables */
HAWK_ASSERT (!"should never happen - wrong target for reset");
hawk_rtx_seterrnum (rtx, &var->loc, HAWK_EBADARG);
return -1;
}
}
static int run_reset (hawk_rtx_t* rtx, hawk_nde_reset_t* nde)
{
return reset_variable (rtx, (hawk_nde_var_t*)nde->var);
}
static int run_print (hawk_rtx_t* rtx, hawk_nde_print_t* nde)
{
hawk_ooch_t* out = HAWK_NULL;
hawk_val_t* out_v = HAWK_NULL;
const hawk_ooch_t* dst;
int n, xret = 0;
HAWK_ASSERT (
(nde->out_type == HAWK_OUT_PIPE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_RWPIPE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_FILE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_APFILE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_CONSOLE && nde->out == HAWK_NULL));
/* check if destination has been specified. */
if (nde->out)
{
hawk_oow_t len, i;
/* if so, resolve the destination name */
out_v = eval_expression(rtx, nde->out);
if (!out_v) return -1;
hawk_rtx_refupval (rtx, out_v);
out = hawk_rtx_getvaloocstr(rtx, out_v, &len);
if (!out) goto oops;
if (len <= 0)
{
/* the destination name is empty */
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EIONMEM, HAWK_T("empty I/O name"));
goto oops;
}
/* it needs to check if the destination name contains
* any invalid characters to the underlying system */
for (i = 0; i < len; i++)
{
if (out[i] == HAWK_T('\0'))
{
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EIONMNL, HAWK_T("invalid I/O name of length %zu containing '\\0'"), len);
goto oops_1;
}
}
}
/* transforms the destination to suit the usage with io */
dst = (out == HAWK_NULL)? HAWK_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 = hawk_rtx_writeiostr(rtx, nde->out_type, dst, HAWK_OOECS_PTR(&rtx->inrec.line), HAWK_OOECS_LEN(&rtx->inrec.line));
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT)
{
xret = PRINT_IOERR;
}
else
{
goto oops;
}
}
}
else
{
/* if it has any arguments, print the arguments separated by
* the value OFS */
hawk_nde_t* head, * np;
hawk_val_t* v;
if (nde->args->type == HAWK_NDE_GRP)
{
/* parenthesized print */
HAWK_ASSERT (nde->args->next == HAWK_NULL);
head = ((hawk_nde_grp_t*)nde->args)->body;
}
else head = nde->args;
for (np = head; np != HAWK_NULL; np = np->next)
{
if (np != head)
{
n = hawk_rtx_writeiostr(rtx, nde->out_type, dst, rtx->gbl.ofs.ptr, rtx->gbl.ofs.len);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT)
{
xret = PRINT_IOERR;
}
else
{
goto oops;
}
}
}
v = eval_expression(rtx, np);
if (v == HAWK_NULL) goto oops_1;
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_writeioval(rtx, nde->out_type, dst, v);
hawk_rtx_refdownval (rtx, v);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT)
{
xret = PRINT_IOERR;
}
else
{
goto oops;
}
}
}
}
/* print the value ORS to terminate the operation */
n = hawk_rtx_writeiostr(rtx, nde->out_type, dst, rtx->gbl.ors.ptr, rtx->gbl.ors.len);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT)
{
xret = PRINT_IOERR;
}
else
{
goto oops;
}
}
/* unlike printf, flushio() is not needed here as print
* inserts <NL> that triggers auto-flush */
if (out_v)
{
if (out) hawk_rtx_freevaloocstr (rtx, out_v, out);
hawk_rtx_refdownval (rtx, out_v);
}
return xret;
oops:
ADJERR_LOC (rtx, &nde->loc);
oops_1:
if (out_v)
{
if (out) hawk_rtx_freevaloocstr (rtx, out_v, out);
hawk_rtx_refdownval (rtx, out_v);
}
return -1;
}
static int run_printf (hawk_rtx_t* rtx, hawk_nde_print_t* nde)
{
hawk_ooch_t* out = HAWK_NULL;
hawk_val_t* out_v = HAWK_NULL;
hawk_val_t* v;
hawk_val_type_t vtype;
const hawk_ooch_t* dst;
hawk_nde_t* head;
int n, xret = 0;
HAWK_ASSERT (
(nde->out_type == HAWK_OUT_PIPE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_RWPIPE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_FILE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_APFILE && nde->out != HAWK_NULL) ||
(nde->out_type == HAWK_OUT_CONSOLE && nde->out == HAWK_NULL));
if (nde->out)
{
hawk_oow_t len, i;
out_v = eval_expression(rtx, nde->out);
if (!out_v) return -1;
hawk_rtx_refupval (rtx, out_v);
out = hawk_rtx_getvaloocstr(rtx, out_v, &len);
if (!out) goto oops;
if (len <= 0)
{
/* the output destination name is empty. */
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EIONMEM, HAWK_T("empty I/O name"));
goto oops_1;
}
for (i = 0; i < len; i++)
{
if (out[i] == HAWK_T('\0'))
{
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EIONMNL, HAWK_T("invalid I/O name of length %zu containing '\\0'"), len);
/* the output destination name contains a null
* character. */
goto oops_1;
}
}
}
dst = (out == HAWK_NULL)? HAWK_T(""): out;
/*( valid printf statement should have at least one argument. the parser must ensure this. */
HAWK_ASSERT (nde->args != HAWK_NULL);
if (nde->args->type == HAWK_NDE_GRP)
{
/* parenthesized print */
HAWK_ASSERT (nde->args->next == HAWK_NULL);
head = ((hawk_nde_grp_t*)nde->args)->body;
}
else head = nde->args;
/* valid printf statement should have at least one argument. the parser must ensure this */
HAWK_ASSERT (head != HAWK_NULL);
v = eval_expression(rtx, head);
if (v == HAWK_NULL) goto oops_1;
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
switch (vtype)
{
case HAWK_VAL_STR:
/* perform the formatted output */
n = output_formatted(rtx, nde->out_type, dst, ((hawk_val_str_t*)v)->val.ptr, ((hawk_val_str_t*)v)->val.len, head->next);
hawk_rtx_refdownval (rtx, v);
if (n <= -1)
{
if (n == PRINT_IOERR) xret = n;
else goto oops;
}
break;
case HAWK_VAL_MBS:
/* perform the formatted output */
n = output_formatted_bytes(rtx, nde->out_type, dst, ((hawk_val_mbs_t*)v)->val.ptr, ((hawk_val_mbs_t*)v)->val.len, head->next);
hawk_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 = hawk_rtx_writeioval(rtx, nde->out_type, dst, v);
hawk_rtx_refdownval (rtx, v);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT) xret = PRINT_IOERR;
else goto oops;
}
break;
}
if (hawk_rtx_flushio(rtx, nde->out_type, dst) <= -1)
{
if (rtx->awk->opt.trait & HAWK_TOLERANT) xret = PRINT_IOERR;
else goto oops_1;
}
if (out_v)
{
if (out) hawk_rtx_freevaloocstr (rtx, out_v, out);
hawk_rtx_refdownval (rtx, out_v);
}
return xret;
oops:
ADJERR_LOC (rtx, &nde->loc);
oops_1:
if (out_v)
{
if (out) hawk_rtx_freevaloocstr (rtx, out_v, out);
hawk_rtx_refdownval (rtx, out_v);
}
return -1;
}
static int output_formatted (
hawk_rtx_t* rtx, int out_type, const hawk_ooch_t* dst,
const hawk_ooch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args)
{
hawk_ooch_t* ptr;
hawk_oow_t len;
int n;
ptr = hawk_rtx_format(rtx, HAWK_NULL, HAWK_NULL, fmt, fmt_len, 0, args, &len);
if (!ptr) return -1;
n = hawk_rtx_writeiostr(rtx, out_type, dst, ptr, len);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
return (rtx->awk->opt.trait & HAWK_TOLERANT)? PRINT_IOERR: -1;
}
return 0;
}
static int output_formatted_bytes (
hawk_rtx_t* rtx, int out_type, const hawk_ooch_t* dst,
const hawk_bch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args)
{
hawk_bch_t* ptr;
hawk_oow_t len;
int n;
ptr = hawk_rtx_formatmbs(rtx, HAWK_NULL, HAWK_NULL, fmt, fmt_len, 0, args, &len);
if (!ptr) return -1;
n = hawk_rtx_writeiobytes(rtx, out_type, dst, ptr, len);
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
{
return (rtx->awk->opt.trait & HAWK_TOLERANT)? PRINT_IOERR: -1;
}
return 0;
}
static hawk_val_t* eval_expression (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* v;
int n;
#if 0
if (rtx->exit_level >= EXIT_GLOBAL)
{
/* returns HAWK_NULL as if an error occurred but
* clears the error number. run_main will
* detect this condition and treat it as a
* non-error condition.*/
rtx->errinf.num = HAWK_ENOERR;
return HAWK_NULL;
}
#endif
v = eval_expression0 (rtx, nde);
if (v == HAWK_NULL) return HAWK_NULL;
if (HAWK_RTX_GETVALTYPE (rtx, v) == HAWK_VAL_REX)
{
hawk_oocs_t vs;
/* special case where a regular expression is used in
* without any match operators:
* print /abc/;
* perform match against $0.
*/
hawk_rtx_refupval (rtx, v);
if (HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == HAWK_VAL_NIL)
{
/* the record has never been read.
* probably, this function has been triggered
* by the statements in the BEGIN block */
vs.ptr = HAWK_T("");
vs.len = 0;
}
else
{
/* the internal value representing $0 should always be of the string type once it has been set/updated. it is nil initially. */
HAWK_ASSERT (HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == HAWK_VAL_STR);
vs.ptr = ((hawk_val_str_t*)rtx->inrec.d0)->val.ptr;
vs.len = ((hawk_val_str_t*)rtx->inrec.d0)->val.len;
}
n = hawk_rtx_matchval(rtx, v, &vs, &vs, HAWK_NULL, HAWK_NULL);
if (n <= -1)
{
ADJERR_LOC (rtx, &nde->loc);
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, v);
v = hawk_rtx_makeintval(rtx, (n != 0));
if (v == HAWK_NULL)
{
/* adjust error line */
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
}
return v;
}
static hawk_val_t* eval_expression0 (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
static eval_expr_t __evaluator[] =
{
/* the order of functions here should match the order
* of node types(hawk_nde_type_t) declared in hawk.h */
eval_group,
eval_assignment,
eval_binary,
eval_unary,
eval_incpre,
eval_incpst,
eval_cnd,
eval_fncall_fnc,
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
};
hawk_val_t* v;
HAWK_ASSERT (nde->type >= HAWK_NDE_GRP && (nde->type - HAWK_NDE_GRP) < HAWK_COUNTOF(__evaluator));
v = __evaluator[nde->type-HAWK_NDE_GRP](rtx, nde);
if (v != HAWK_NULL && rtx->exit_level >= EXIT_GLOBAL)
{
hawk_rtx_refupval (rtx, v);
hawk_rtx_refdownval (rtx, v);
/* returns HAWK_NULL as if an error occurred but
* clears the error number. run_main will
* detect this condition and treat it as a
* non-error condition.*/
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_ENOERR);
return HAWK_NULL;
}
return v;
}
static hawk_val_t* eval_group (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
#if 0
/* eval_binop_in evaluates the HAWK_NDE_GRP specially.
* so this function should never be reached. */
HAWK_ASSERT (!"should never happen - NDE_GRP only for in");
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EINTERN);
return HAWK_NULL;
#endif
/* a group can be evauluated in a normal context
* if a program is parsed with HAWK_TOLERANT on.
* before the introduction of this option, the grouped
* expression was valid only coerced with the 'in'
* operator.
* */
/* when a group is evaluated in a normal context,
* we return the last expression as a value. */
hawk_val_t* val;
hawk_nde_t* np;
np = ((hawk_nde_grp_t*)nde)->body;
HAWK_ASSERT (np != HAWK_NULL);
loop:
val = eval_expression (rtx, np);
if (val == HAWK_NULL) return HAWK_NULL;
np = np->next;
if (np)
{
hawk_rtx_refupval (rtx, val);
hawk_rtx_refdownval (rtx, val);
goto loop;
}
return val;
}
static hawk_val_t* eval_assignment (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val, * ret;
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)nde;
HAWK_ASSERT (ass->left != HAWK_NULL);
HAWK_ASSERT (ass->right != HAWK_NULL);
HAWK_ASSERT (ass->right->next == HAWK_NULL);
val = eval_expression (rtx, ass->right);
if (val == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, val);
if (ass->opcode != HAWK_ASSOP_NONE)
{
hawk_val_t* val2, * tmp;
static binop_func_t binop_func[] =
{
/* this table must match hawk_assop_type_t in rtx.h */
HAWK_NULL, /* HAWK_ASSOP_NONE */
eval_binop_plus,
eval_binop_minus,
eval_binop_mul,
eval_binop_div,
eval_binop_idiv,
eval_binop_mod,
eval_binop_exp,
eval_binop_concat,
eval_binop_rshift,
eval_binop_lshift,
eval_binop_band,
eval_binop_bxor,
eval_binop_bor
};
HAWK_ASSERT (ass->left->next == HAWK_NULL);
val2 = eval_expression(rtx, ass->left);
if (val2 == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, val);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, val2);
HAWK_ASSERT (ass->opcode >= 0);
HAWK_ASSERT (ass->opcode < HAWK_COUNTOF(binop_func));
HAWK_ASSERT (binop_func[ass->opcode] != HAWK_NULL);
tmp = binop_func[ass->opcode](rtx, val2, val);
if (tmp == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, val2);
hawk_rtx_refdownval (rtx, val);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, val2);
hawk_rtx_refdownval (rtx, val);
val = tmp;
hawk_rtx_refupval (rtx, val);
}
ret = do_assignment(rtx, ass->left, val);
hawk_rtx_refdownval (rtx, val);
return ret;
}
static hawk_val_t* do_assignment (hawk_rtx_t* rtx, hawk_nde_t* var, hawk_val_t* val)
{
hawk_val_t* ret;
hawk_errnum_t errnum;
switch (var->type)
{
case HAWK_NDE_NAMED:
case HAWK_NDE_GBL:
case HAWK_NDE_LCL:
case HAWK_NDE_ARG:
ret = do_assignment_nonidx(rtx, (hawk_nde_var_t*)var, val);
break;
case HAWK_NDE_NAMEDIDX:
case HAWK_NDE_GBLIDX:
case HAWK_NDE_LCLIDX:
case HAWK_NDE_ARGIDX:
if (HAWK_RTX_GETVALTYPE(rtx, val) == HAWK_VAL_MAP)
{
/* a map cannot become a member of a map */
errnum = HAWK_EMAPTOIDX;
goto exit_on_error;
}
ret = do_assignment_idx(rtx, (hawk_nde_var_t*)var, val);
break;
case HAWK_NDE_POS:
if (HAWK_RTX_GETVALTYPE(rtx, val) == HAWK_VAL_MAP)
{
/* a map cannot be assigned to a positional */
errnum = HAWK_EMAPTOPOS;
goto exit_on_error;
}
ret = do_assignment_pos(rtx, (hawk_nde_pos_t*)var, val);
break;
default:
HAWK_ASSERT (!"should never happen - invalid variable type");
errnum = HAWK_EINTERN;
goto exit_on_error;
}
return ret;
exit_on_error:
hawk_rtx_seterrnum (rtx, &var->loc, errnum);
return HAWK_NULL;
}
static hawk_val_t* do_assignment_nonidx (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val)
{
hawk_val_type_t vtype;
HAWK_ASSERT (
var->type == HAWK_NDE_NAMED ||
var->type == HAWK_NDE_GBL ||
var->type == HAWK_NDE_LCL ||
var->type == HAWK_NDE_ARG
);
HAWK_ASSERT (var->idx == HAWK_NULL);
vtype = HAWK_RTX_GETVALTYPE (rtx, val);
switch (var->type)
{
case HAWK_NDE_NAMED:
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->named, var->id.name.ptr, var->id.name.len);
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP))
{
if (pair && HAWK_RTX_GETVALTYPE(rtx, (hawk_val_t*)HAWK_HTB_VPTR(pair)) == HAWK_VAL_MAP)
{
/* old value is a map - it can only be accessed through indexing. */
if (vtype == HAWK_VAL_MAP)
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOMAP, HAWK_T("not allowed to change a map '%.*js' to another map"), var->id.name.len, var->id.name.ptr);
else
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOSCALAR, HAWK_T("not allowed to change a map '%.*js' to a scalar"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
else if (vtype == HAWK_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. */
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOVAR, HAWK_T("not allowed to assign a map to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
}
if (hawk_htb_upsert(rtx->named, var->id.name.ptr, var->id.name.len, val, 0) == HAWK_NULL)
{
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, val);
break;
}
case HAWK_NDE_GBL:
{
if (set_global(rtx, var->id.idxa, var, val, 1) == -1)
{
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
break;
}
case HAWK_NDE_LCL:
{
hawk_val_t* old = RTX_STACK_LCL(rtx,var->id.idxa);
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP))
{
if (HAWK_RTX_GETVALTYPE(rtx, old) == HAWK_VAL_MAP)
{
/* old value is a map - it can only be accessed through indexing. */
if (vtype == HAWK_VAL_MAP)
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOMAP, HAWK_T("not allowed to change a map '%.*js' to another map"), var->id.name.len, var->id.name.ptr);
else
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOSCALAR, HAWK_T("not allowed to change a map '%.*js' to a scalar"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
else if (vtype == HAWK_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. */
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOVAR, HAWK_T("not allowed to assign a map to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
}
hawk_rtx_refdownval (rtx, old);
RTX_STACK_LCL(rtx,var->id.idxa) = val;
hawk_rtx_refupval (rtx, val);
break;
}
case HAWK_NDE_ARG:
{
hawk_val_t* old = RTX_STACK_ARG(rtx,var->id.idxa);
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP))
{
if (HAWK_RTX_GETVALTYPE(rtx, old) == HAWK_VAL_MAP)
{
/* old value is a map - it can only be accessed through indexing. */
if (vtype == HAWK_VAL_MAP)
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOMAP, HAWK_T("not allowed to change a map '%.*js' to another map"), var->id.name.len, var->id.name.ptr);
else
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOSCALAR, HAWK_T("not allowed to change a map '%.*js' to a scalar"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
else if (vtype == HAWK_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. */
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_EMAPTOVAR, HAWK_T("not allowed to assign a map to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
}
hawk_rtx_refdownval (rtx, old);
RTX_STACK_ARG(rtx,var->id.idxa) = val;
hawk_rtx_refupval (rtx, val);
break;
}
}
return val;
}
static hawk_val_t* do_assignment_idx (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val)
{
hawk_val_map_t* map;
hawk_val_type_t mvtype;
hawk_ooch_t* str;
hawk_oow_t len;
hawk_ooch_t idxbuf[IDXBUFSIZE];
HAWK_ASSERT (
(var->type == HAWK_NDE_NAMEDIDX ||
var->type == HAWK_NDE_GBLIDX ||
var->type == HAWK_NDE_LCLIDX ||
var->type == HAWK_NDE_ARGIDX) && var->idx != HAWK_NULL);
HAWK_ASSERT (HAWK_RTX_GETVALTYPE (rtx, val) != HAWK_VAL_MAP);
retry:
switch (var->type)
{
case HAWK_NDE_NAMEDIDX:
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->named, var->id.name.ptr, var->id.name.len);
map = (pair == HAWK_NULL)? (hawk_val_map_t*)hawk_val_nil: (hawk_val_map_t*)HAWK_HTB_VPTR(pair);
break;
}
case HAWK_NDE_GBLIDX:
map = (hawk_val_map_t*)RTX_STACK_GBL(rtx,var->id.idxa);
break;
case HAWK_NDE_LCLIDX:
map = (hawk_val_map_t*)RTX_STACK_LCL(rtx,var->id.idxa);
break;
default: /* HAWK_NDE_ARGIDX */
map = (hawk_val_map_t*)RTX_STACK_ARG(rtx,var->id.idxa);
break;
}
mvtype = HAWK_RTX_GETVALTYPE(rtx, map);
if (mvtype == HAWK_VAL_NIL)
{
hawk_val_t* tmp;
/* the map is not initialized yet */
tmp = hawk_rtx_makemapval(rtx);
if (tmp == HAWK_NULL)
{
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
switch (var->type)
{
case HAWK_NDE_NAMEDIDX:
{
/* doesn't have to decrease the reference count
* of the previous value here as it is done by
* hawk_htb_upsert */
hawk_rtx_refupval (rtx, tmp);
if (hawk_htb_upsert(rtx->named, var->id.name.ptr, var->id.name.len, tmp, 0) == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, tmp);
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
break;
}
case HAWK_NDE_GBLIDX:
{
int x;
hawk_rtx_refupval (rtx, tmp);
x = hawk_rtx_setgbl(rtx, (int)var->id.idxa, tmp);
hawk_rtx_refdownval (rtx, tmp);
if (x <= -1)
{
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
break;
}
case HAWK_NDE_LCLIDX:
hawk_rtx_refdownval (rtx, (hawk_val_t*)map);
RTX_STACK_LCL(rtx,var->id.idxa) = tmp;
hawk_rtx_refupval (rtx, tmp);
break;
default: /* HAWK_NDE_ARGIDX */
hawk_rtx_refdownval (rtx, (hawk_val_t*)map);
RTX_STACK_ARG(rtx,var->id.idxa) = tmp;
hawk_rtx_refupval (rtx, tmp);
break;
}
map = (hawk_val_map_t*)tmp;
}
else if (mvtype != HAWK_VAL_MAP)
{
if (rtx->awk->opt.trait & HAWK_FLEXMAP)
{
/* if FLEXMAP is on, you can switch a scalar value to a map */
hawk_nde_var_t fake;
/* create a fake non-indexed variable node */
fake = *var;
/* NOTE: type conversion by decrementing by 4 is
* dependent on the hawk_nde_type_t
* enumerators defined in <hawk.h> */
fake.type = var->type - 4;
fake.idx = HAWK_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. */
hawk_rtx_seterrfmt (rtx, &var->loc, HAWK_ESCALARTOMAP, HAWK_T("not allowed to change a scalar '%.*js' to a map"), var->id.name.len, var->id.name.ptr);
return HAWK_NULL;
}
}
len = HAWK_COUNTOF(idxbuf);
str = idxnde_to_str(rtx, var->idx, idxbuf, &len);
if (str == HAWK_NULL) return HAWK_NULL;
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("**** index str=>%s, map->ref=%d, map->type=%d\n"), str, (int)map->ref, (int)map->type);
#endif
if (hawk_htb_upsert(map->map, str, len, val, 0) == HAWK_NULL)
{
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
ADJERR_LOC (rtx, &var->loc);
return HAWK_NULL;
}
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
hawk_rtx_refupval (rtx, val);
return val;
}
static hawk_val_t* do_assignment_pos (hawk_rtx_t* rtx, hawk_nde_pos_t* pos, hawk_val_t* val)
{
hawk_val_t* v;
hawk_val_type_t vtype;
hawk_int_t lv;
hawk_oocs_t str;
int n;
v = eval_expression (rtx, pos->val);
if (v == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint (rtx, v, &lv);
hawk_rtx_refdownval (rtx, v);
if (n <= -1)
{
hawk_rtx_seterrnum (rtx, &pos->loc, HAWK_EPOSIDX);
return HAWK_NULL;
}
if (!IS_VALID_POSIDX(lv))
{
hawk_rtx_seterrnum (rtx, &pos->loc, HAWK_EPOSIDX);
return HAWK_NULL;
}
vtype = HAWK_RTX_GETVALTYPE (rtx, val);
if (vtype == HAWK_VAL_STR)
{
str = ((hawk_val_str_t*)val)->val;
}
else
{
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, val, &out) <= -1)
{
ADJERR_LOC (rtx, &pos->loc);
return HAWK_NULL;
}
str = out.u.cpldup;
}
n = hawk_rtx_setrec(rtx, (hawk_oow_t)lv, &str);
if (vtype == HAWK_VAL_STR)
{
/* do nothing */
}
else
{
hawk_rtx_freemem (rtx, str.ptr);
}
if (n <= -1) return HAWK_NULL;
return (lv == 0)? rtx->inrec.d0: rtx->inrec.flds[lv-1].val;
}
static hawk_val_t* eval_binary (hawk_rtx_t* run, hawk_nde_t* nde)
{
static binop_func_t binop_func[] =
{
/* the order of the functions should be inline with
* the operator declaration in run.h */
HAWK_NULL, /* eval_binop_lor */
HAWK_NULL, /* eval_binop_land */
HAWK_NULL, /* eval_binop_in */
eval_binop_bor,
eval_binop_bxor,
eval_binop_band,
eval_binop_teq,
eval_binop_tne,
eval_binop_eq,
eval_binop_ne,
eval_binop_gt,
eval_binop_ge,
eval_binop_lt,
eval_binop_le,
eval_binop_lshift,
eval_binop_rshift,
eval_binop_plus,
eval_binop_minus,
eval_binop_mul,
eval_binop_div,
eval_binop_idiv,
eval_binop_mod,
eval_binop_exp,
eval_binop_concat,
HAWK_NULL, /* eval_binop_ma */
HAWK_NULL /* eval_binop_nm */
};
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
hawk_val_t* left, * right, * res;
HAWK_ASSERT (exp->type == HAWK_NDE_EXP_BIN);
if (exp->opcode == HAWK_BINOP_LAND)
{
res = eval_binop_land (run, exp->left, exp->right);
}
else if (exp->opcode == HAWK_BINOP_LOR)
{
res = eval_binop_lor (run, exp->left, exp->right);
}
else if (exp->opcode == HAWK_BINOP_IN)
{
/* treat the in operator specially */
res = eval_binop_in (run, exp->left, exp->right);
}
else if (exp->opcode == HAWK_BINOP_NM)
{
res = eval_binop_nm (run, exp->left, exp->right);
}
else if (exp->opcode == HAWK_BINOP_MA)
{
res = eval_binop_ma (run, exp->left, exp->right);
}
else
{
HAWK_ASSERT (exp->left->next == HAWK_NULL);
left = eval_expression (run, exp->left);
if (left == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, left);
HAWK_ASSERT (exp->right->next == HAWK_NULL);
right = eval_expression (run, exp->right);
if (right == HAWK_NULL)
{
hawk_rtx_refdownval (run, left);
return HAWK_NULL;
}
hawk_rtx_refupval (run, right);
HAWK_ASSERT (exp->opcode >= 0 &&
exp->opcode < HAWK_COUNTOF(binop_func));
HAWK_ASSERT (binop_func[exp->opcode] != HAWK_NULL);
res = binop_func[exp->opcode] (run, left, right);
if (res == HAWK_NULL) ADJERR_LOC (run, &nde->loc);
hawk_rtx_refdownval (run, left);
hawk_rtx_refdownval (run, right);
}
return res;
}
static hawk_val_t* eval_binop_lor (
hawk_rtx_t* run, hawk_nde_t* left, hawk_nde_t* right)
{
/*
hawk_val_t* res = HAWK_NULL;
res = hawk_rtx_makeintval (
run,
hawk_rtx_valtobool(run,left) ||
hawk_rtx_valtobool(run,right)
);
if (res == HAWK_NULL)
{
ADJERR_LOC (run, &left->loc);
return HAWK_NULL;
}
return res;
*/
/* short-circuit evaluation required special treatment */
hawk_val_t* lv, * rv, * res;
HAWK_ASSERT (left->next == HAWK_NULL);
lv = eval_expression (run, left);
if (lv == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, lv);
if (hawk_rtx_valtobool(run, lv))
{
res = HAWK_VAL_ONE;
}
else
{
HAWK_ASSERT (right->next == HAWK_NULL);
rv = eval_expression (run, right);
if (rv == HAWK_NULL)
{
hawk_rtx_refdownval (run, lv);
return HAWK_NULL;
}
hawk_rtx_refupval (run, rv);
res = hawk_rtx_valtobool(run,rv)?
HAWK_VAL_ONE: HAWK_VAL_ZERO;
hawk_rtx_refdownval (run, rv);
}
hawk_rtx_refdownval (run, lv);
return res;
}
static hawk_val_t* eval_binop_land (hawk_rtx_t* run, hawk_nde_t* left, hawk_nde_t* right)
{
/*
hawk_val_t* res = HAWK_NULL;
res = hawk_rtx_makeintval (
run,
hawk_rtx_valtobool(run,left) &&
hawk_rtx_valtobool(run,right)
);
if (res == HAWK_NULL)
{
ADJERR_LOC (run, &left->loc);
return HAWK_NULL;
}
return res;
*/
/* short-circuit evaluation required special treatment */
hawk_val_t* lv, * rv, * res;
HAWK_ASSERT (left->next == HAWK_NULL);
lv = eval_expression (run, left);
if (lv == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, lv);
if (!hawk_rtx_valtobool(run, lv))
{
res = HAWK_VAL_ZERO;
}
else
{
HAWK_ASSERT (right->next == HAWK_NULL);
rv = eval_expression (run, right);
if (rv == HAWK_NULL)
{
hawk_rtx_refdownval (run, lv);
return HAWK_NULL;
}
hawk_rtx_refupval (run, rv);
res = hawk_rtx_valtobool(run,rv)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
hawk_rtx_refdownval (run, rv);
}
hawk_rtx_refdownval (run, lv);
return res;
}
static hawk_val_t* eval_binop_in (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
{
hawk_val_t* rv;
hawk_val_type_t rvtype;
hawk_ooch_t* str;
hawk_oow_t len;
hawk_ooch_t idxbuf[IDXBUFSIZE];
if (right->type != HAWK_NDE_GBL &&
right->type != HAWK_NDE_LCL &&
right->type != HAWK_NDE_ARG &&
right->type != HAWK_NDE_NAMED)
{
/* the compiler should have handled this case */
HAWK_ASSERT (!"should never happen - it needs a plain variable");
hawk_rtx_seterrnum (rtx, &right->loc, HAWK_EINTERN);
return HAWK_NULL;
}
/* evaluate the left-hand side of the operator */
len = HAWK_COUNTOF(idxbuf);
str = (left->type == HAWK_NDE_GRP)?
idxnde_to_str (rtx, ((hawk_nde_grp_t*)left)->body, idxbuf, &len):
idxnde_to_str (rtx, left, idxbuf, &len);
if (str == HAWK_NULL) return HAWK_NULL;
/* evaluate the right-hand side of the operator */
HAWK_ASSERT (right->next == HAWK_NULL);
rv = eval_expression (rtx, right);
if (rv == HAWK_NULL)
{
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, rv);
rvtype = HAWK_RTX_GETVALTYPE (rtx, rv);
if (rvtype == HAWK_VAL_NIL)
{
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
hawk_rtx_refdownval (rtx, rv);
return HAWK_VAL_ZERO;
}
else if (rvtype == HAWK_VAL_MAP)
{
hawk_val_t* res;
hawk_htb_t* map;
map = ((hawk_val_map_t*)rv)->map;
res = (hawk_htb_search(map, str, len) == HAWK_NULL)? HAWK_VAL_ZERO: HAWK_VAL_ONE;
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
hawk_rtx_refdownval (rtx, rv);
return res;
}
/* need a map */
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
hawk_rtx_refdownval (rtx, rv);
hawk_rtx_seterrnum (rtx, &right->loc, HAWK_ENOTMAPNILIN);
return HAWK_NULL;
}
static hawk_val_t* eval_binop_bor (
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_int_t l1, l2;
if (hawk_rtx_valtoint (rtx, left, &l1) <= -1 ||
hawk_rtx_valtoint (rtx, right, &l2) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
return hawk_rtx_makeintval (rtx, l1 | l2);
}
static hawk_val_t* eval_binop_bxor (
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_int_t l1, l2;
if (hawk_rtx_valtoint (rtx, left, &l1) <= -1 ||
hawk_rtx_valtoint (rtx, right, &l2) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
return hawk_rtx_makeintval (rtx, l1 ^ l2);
}
static hawk_val_t* eval_binop_band (
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_int_t l1, l2;
if (hawk_rtx_valtoint (rtx, left, &l1) <= -1 ||
hawk_rtx_valtoint (rtx, right, &l2) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
return hawk_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 HAWK_INLINE cmp_op_t inverse_cmp_op (cmp_op_t op)
{
static cmp_op_t inverse_cmp_op_tab[] =
{
CMP_OP_NONE,
CMP_OP_NE,
CMP_OP_EQ,
CMP_OP_LT,
CMP_OP_LE,
CMP_OP_GT,
CMP_OP_GE
};
return inverse_cmp_op_tab[op];
}
static HAWK_INLINE int __cmp_ensure_not_equal (hawk_rtx_t* rtx, cmp_op_t op_hint)
{
/* checking equality is mostly obvious. however, it is not possible
* to test if one is less/greater than the other for some operands.
* this function return a number that ensures to make NE to true and
* all other operations false */
switch (op_hint)
{
case CMP_OP_EQ:
case CMP_OP_NE:
return 1; /* not equal */
case CMP_OP_GT:
case CMP_OP_LT:
return 0; /* make GT or LT to be false by claiming equal */
case CMP_OP_GE:
return -1; /* make GE false by claiming less */
case CMP_OP_LE:
return 1; /* make LE false by claiming greater */
default:
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return -1;
}
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_nil_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return 0;
}
static HAWK_INLINE int __cmp_nil_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_int_t v = HAWK_RTX_GETINTFROMVAL (rtx, right);
return (v < 0)? 1: ((v > 0)? -1: 0);
}
static HAWK_INLINE int __cmp_nil_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
if (((hawk_val_flt_t*)right)->val < 0) return 1;
if (((hawk_val_flt_t*)right)->val > 0) return -1;
return 0;
}
static HAWK_INLINE int __cmp_nil_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (((hawk_val_str_t*)right)->val.len == 0)? 0: -1;
}
static HAWK_INLINE int __cmp_nil_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (((hawk_val_mbs_t*)right)->val.len == 0)? 0: -1;
}
static HAWK_INLINE int __cmp_nil_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
/* != -> true, all others -> false */
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_nil_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (HAWK_HTB_SIZE(((hawk_val_map_t*)right)->map) == 0)? 0: -1;
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_int_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_int_t v = HAWK_RTX_GETINTFROMVAL(rtx, left);
return (v > 0)? 1: ((v < 0)? -1: 0);
}
static HAWK_INLINE int __cmp_int_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL(rtx, right);
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
}
static HAWK_INLINE int __cmp_int_flt (
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL (rtx, left);
if (v1 > ((hawk_val_flt_t*)right)->val) return 1;
if (v1 < ((hawk_val_flt_t*)right)->val) return -1;
return 0;
}
static HAWK_INLINE int __cmp_int_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_t* hawk = hawk_rtx_gethawk(rtx);
hawk_ooch_t* str0;
hawk_oow_t len0;
int n;
/* SCO CC doesn't seem to handle right->nstr > 0 properly */
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->nstr /*> 0*/)
{
hawk_int_t ll, v1;
hawk_flt_t rr;
n = hawk_oochars_to_num(
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, (hawk->opt.trait & HAWK_STRIPSTRSPC), 0),
((hawk_val_str_t*)right)->val.ptr,
((hawk_val_str_t*)right)->val.len,
&ll, &rr
);
v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
if (n == 0)
{
/* a numeric integral string */
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
}
else if (n > 0)
{
/* a numeric floating-point string */
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
}
}
str0 = hawk_rtx_valtooocstrdup(rtx, left, &len0);
if (!str0) return CMP_ERROR;
n = hawk_comp_oochars(str0, len0, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
hawk_rtx_freemem (rtx, str0);
return n;
}
static HAWK_INLINE int __cmp_int_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_t* hawk = hawk_rtx_gethawk(rtx);
hawk_bch_t* str0;
hawk_oow_t len0;
int n;
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->nstr /*> 0*/)
{
hawk_int_t ll, v1;
hawk_flt_t rr;
n = hawk_bchars_to_num (
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, (hawk->opt.trait & HAWK_STRIPSTRSPC), 0),
((hawk_val_mbs_t*)right)->val.ptr,
((hawk_val_mbs_t*)right)->val.len,
&ll, &rr
);
v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
if (n == 0)
{
/* a numeric integral string */
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
}
else if (n > 0)
{
/* a numeric floating-point string */
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
}
}
str0 = hawk_rtx_valtobcstrdup(rtx, left, &len0);
if (!str0) return -1;
n = hawk_comp_bchars(str0, len0, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
hawk_rtx_freemem (rtx, str0);
return n;
}
static HAWK_INLINE int __cmp_int_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_int_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
/* compare an integer and the size of a map */
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
hawk_int_t v2 = HAWK_HTB_SIZE(((hawk_val_map_t*)right)->map);
if (v1 > v2) return 1;
if (v1 < v2) return -1;
return 0;
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_flt_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
if (((hawk_val_flt_t*)left)->val > 0) return 1;
if (((hawk_val_flt_t*)left)->val < 0) return -1;
return 0;
}
static HAWK_INLINE int __cmp_flt_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL (rtx, right);
if (((hawk_val_flt_t*)left)->val > v2) return 1;
if (((hawk_val_flt_t*)left)->val < v2) return -1;
return 0;
}
static HAWK_INLINE int __cmp_flt_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
if (((hawk_val_flt_t*)left)->val >
((hawk_val_flt_t*)right)->val) return 1;
if (((hawk_val_flt_t*)left)->val <
((hawk_val_flt_t*)right)->val) return -1;
return 0;
}
static HAWK_INLINE int __cmp_flt_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_t* hawk = hawk_rtx_gethawk(rtx);
hawk_ooch_t* str0;
hawk_oow_t len0;
int n;
/* SCO CC doesn't seem to handle right->nstr > 0 properly */
if (hawk->opt.trait & HAWK_NCMPONSTR || right->nstr /*> 0*/)
{
const hawk_ooch_t* end;
hawk_flt_t rr;
rr = hawk_oochars_to_flt(((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, &end, (hawk->opt.trait & HAWK_STRIPSTRSPC));
if (end == ((hawk_val_str_t*)right)->val.ptr + ((hawk_val_str_t*)right)->val.len)
{
return (((hawk_val_flt_t*)left)->val > rr)? 1:
(((hawk_val_flt_t*)left)->val < rr)? -1: 0;
}
}
str0 = hawk_rtx_valtooocstrdup(rtx, left, &len0);
if (!str0) return CMP_ERROR;
n = hawk_comp_oochars(str0, len0, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
hawk_rtx_freemem (rtx, str0);
return n;
}
static HAWK_INLINE int __cmp_flt_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_t* hawk = hawk_rtx_gethawk(rtx);
hawk_bch_t* str0;
hawk_oow_t len0;
int n;
if (hawk->opt.trait & HAWK_NCMPONSTR || right->nstr /*> 0*/)
{
const hawk_bch_t* end;
hawk_flt_t rr;
rr = hawk_bchars_to_flt(((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, &end, (hawk->opt.trait & HAWK_STRIPSTRSPC));
if (end == ((hawk_val_mbs_t*)right)->val.ptr + ((hawk_val_mbs_t*)right)->val.len)
{
return (((hawk_val_flt_t*)left)->val > rr)? 1:
(((hawk_val_flt_t*)left)->val < rr)? -1: 0;
}
}
str0 = hawk_rtx_valtobcstrdup(rtx, left, &len0);
if (!str0) return CMP_ERROR;
n = hawk_comp_bchars(str0, len0, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
hawk_rtx_freemem (rtx, str0);
return n;
}
static HAWK_INLINE int __cmp_flt_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_flt_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
/* compare a float with the size of a map */
hawk_int_t v2 = HAWK_HTB_SIZE(((hawk_val_map_t*)right)->map);
if (((hawk_val_flt_t*)left)->val > v2) return 1;
if (((hawk_val_flt_t*)left)->val < v2) return -1;
return 0;
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_str_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (((hawk_val_str_t*)left)->val.len == 0)? 0: 1;
}
static HAWK_INLINE int __cmp_str_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_int_str(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_str_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_flt_str(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_str_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_t* hawk = hawk_rtx_gethawk(rtx);
hawk_val_str_t* ls, * rs;
ls = (hawk_val_str_t*)left;
rs = (hawk_val_str_t*)right;
if (ls->nstr == 0 || rs->nstr == 0)
{
/* both are definitely strings */
return hawk_comp_oochars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
}
if (ls->nstr == 1)
{
hawk_int_t ll;
ll = hawk_oochars_to_int(ls->val.ptr, ls->val.len, 0, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
if (rs->nstr == 1)
{
hawk_int_t rr;
rr = hawk_oochars_to_int(rs->val.ptr, rs->val.len, 0, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
return (ll > rr)? 1:
(ll < rr)? -1: 0;
}
else
{
hawk_flt_t rr;
HAWK_ASSERT (rs->nstr == 2);
rr = hawk_oochars_to_flt(rs->val.ptr, rs->val.len, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
return (ll > rr)? 1:
(ll < rr)? -1: 0;
}
}
else
{
hawk_flt_t ll;
HAWK_ASSERT (ls->nstr == 2);
ll = hawk_oochars_to_flt(ls->val.ptr, ls->val.len, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
if (rs->nstr == 1)
{
hawk_int_t rr;
rr = hawk_oochars_to_int(rs->val.ptr, rs->val.len, 0, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
return (ll > rr)? 1:
(ll < rr)? -1: 0;
}
else
{
hawk_flt_t rr;
HAWK_ASSERT (rs->nstr == 2);
rr = hawk_oochars_to_flt(rs->val.ptr, rs->val.len, HAWK_NULL, (hawk->opt.trait & HAWK_STRIPSTRSPC));
return (ll > rr)? 1:
(ll < rr)? -1: 0;
}
}
}
static HAWK_INLINE int __cmp_str_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_val_str_t* ls = (hawk_val_str_t*)left;
hawk_val_mbs_t* rs = (hawk_val_mbs_t*)right;
#if (HAWK_SIZEOF_BCH_T != HAWK_SIZEOF_UINT8_T)
# error Unsupported size of hawk_bch_t
#endif
#if defined(HAWK_OOCH_IS_BCH)
return hawk_comp_bchars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
#else
hawk_bch_t* mbsptr;
hawk_oow_t mbslen;
int n;
mbsptr = hawk_rtx_duputobchars(rtx, ls->val.ptr, ls->val.len, &mbslen);
if (!mbsptr) return CMP_ERROR;
n = hawk_comp_bchars(mbsptr, mbslen, (const hawk_bch_t*)rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
hawk_rtx_freemem (rtx, mbsptr);
return n;
#endif
}
static HAWK_INLINE int __cmp_str_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_str_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_mbs_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (((hawk_val_mbs_t*)left)->val.len == 0)? 0: 1;
}
static HAWK_INLINE int __cmp_mbs_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_int_mbs(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_mbs_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_flt_mbs(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_mbs_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_str_mbs(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_mbs_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
hawk_val_mbs_t* ls = (hawk_val_mbs_t*)left;
hawk_val_mbs_t* rs = (hawk_val_mbs_t*)right;
#if (HAWK_SIZEOF_BCH_T != HAWK_SIZEOF_UINT8_T)
# error Unsupported size of hawk_bch_t
#endif
return hawk_comp_bchars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
}
static HAWK_INLINE int __cmp_mbs_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_mbs_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_fun_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return (((hawk_val_fun_t*)left)->fun == ((hawk_val_fun_t*)right)->fun)? 0: __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_fun_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_map_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_nil_map(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_map_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_int_map(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_map_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
int n;
n = __cmp_flt_map(rtx, right, left, inverse_cmp_op(op_hint));
if (n == CMP_ERROR) return CMP_ERROR;
return -n;
}
static HAWK_INLINE int __cmp_map_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_map_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_map_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
return __cmp_ensure_not_equal(rtx, op_hint);
}
static HAWK_INLINE int __cmp_map_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
{
/* can't compare a map with a map */
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return CMP_ERROR;
}
/* -------------------------------------------------------------------- */
static HAWK_INLINE int __cmp_val (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint, int* ret)
{
int n;
hawk_val_type_t lvtype, rvtype;
typedef int (*cmp_val_t) (hawk_rtx_t*, hawk_val_t*, hawk_val_t*, cmp_op_t op_hint);
static cmp_val_t func[] =
{
/* this table must be synchronized with
* the HAWK_VAL_XXX values in 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 = HAWK_RTX_GETVALTYPE(rtx, left);
rvtype = HAWK_RTX_GETVALTYPE(rtx, right);
if (!(rtx->awk->opt.trait & HAWK_FLEXMAP) &&
(lvtype == HAWK_VAL_MAP || rvtype == HAWK_VAL_MAP))
{
/* a map can't be compared againt other values */
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return -1;
}
HAWK_ASSERT (lvtype >= HAWK_VAL_NIL && lvtype <= HAWK_VAL_MAP);
HAWK_ASSERT (rvtype >= HAWK_VAL_NIL && rvtype <= HAWK_VAL_MAP);
/* mapping fomula and table layout assume:
* HAWK_VAL_NIL = 0
* HAWK_VAL_INT = 1
* HAWK_VAL_FLT = 2
* HAWK_VAL_STR = 3
* HAWK_VAL_MBS = 4
* HAWK_VAL_FUN = 5
* HAWK_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 hawk_rtx_cmpval (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, int* ret)
{
return __cmp_val(rtx, left, right, CMP_OP_NONE, ret);
}
static int teq_val (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
hawk_val_type_t lt, rt;
if (left == right) n = 1;
else
{
lt = HAWK_RTX_GETVALTYPE(rtx, left);
rt = HAWK_RTX_GETVALTYPE(rtx, right);
if (lt != rt) n = 0;
else
{
switch (lt)
{
case HAWK_VAL_NIL:
n = 1;
break;
case HAWK_VAL_INT:
n = (HAWK_RTX_GETINTFROMVAL (rtx, left) == HAWK_RTX_GETINTFROMVAL (rtx, right));
break;
case HAWK_VAL_FLT:
n = ((hawk_val_flt_t*)left)->val == ((hawk_val_flt_t*)right)->val;
break;
case HAWK_VAL_STR:
n = hawk_comp_oochars (
((hawk_val_str_t*)left)->val.ptr,
((hawk_val_str_t*)left)->val.len,
((hawk_val_str_t*)right)->val.ptr,
((hawk_val_str_t*)right)->val.len,
rtx->gbl.ignorecase) == 0;
break;
case HAWK_VAL_MBS:
n = hawk_comp_bchars (
((hawk_val_mbs_t*)left)->val.ptr,
((hawk_val_mbs_t*)left)->val.len,
((hawk_val_mbs_t*)right)->val.ptr,
((hawk_val_mbs_t*)right)->val.len,
rtx->gbl.ignorecase) == 0;
break;
case HAWK_VAL_FUN:
n = ((hawk_val_fun_t*)left)->fun == ((hawk_val_fun_t*)right)->fun;
break;
default:
/* map-x and map-y are always different regardless of
* their contents. however, if they are pointing to the
* same map value, it won't reach here but will be
* handled by the first check in this function */
n = 0;
break;
}
}
}
return n;
}
static hawk_val_t* eval_binop_teq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
return teq_val(rtx, left, right)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_tne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
return teq_val(rtx, left, right)? HAWK_VAL_ZERO: HAWK_VAL_ONE;
}
static hawk_val_t* eval_binop_eq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_EQ, &n) <= -1) return HAWK_NULL;
return (n == 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_ne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_NE, &n) <= -1) return HAWK_NULL;
return (n != 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_gt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_GT, &n) <= -1) return HAWK_NULL;
return (n > 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_ge (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_GE, &n) <= -1) return HAWK_NULL;
return (n >= 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_lt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_LT, &n) <= -1) return HAWK_NULL;
return (n < 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_le (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n;
if (__cmp_val(rtx, left, right, CMP_OP_LE, &n) <= -1) return HAWK_NULL;
return (n <= 0)? HAWK_VAL_ONE: HAWK_VAL_ZERO;
}
static hawk_val_t* eval_binop_lshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_int_t l1, l2;
if (hawk_rtx_valtoint(rtx, left, &l1) <= -1 ||
hawk_rtx_valtoint(rtx, right, &l2) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
return hawk_rtx_makeintval(rtx, l1 << l2);
}
static hawk_val_t* eval_binop_rshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_int_t l1, l2;
if (hawk_rtx_valtoint(rtx, left, &l1) <= -1 ||
hawk_rtx_valtoint(rtx, right, &l2) <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
return hawk_rtx_makeintval (rtx, l1 >> l2);
}
static hawk_val_t* eval_binop_plus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
/*
n1 n2 n3
0 0 = 0
1 0 = 1
0 1 = 2
1 1 = 3
*/
n3 = n1 + (n2 << 1);
HAWK_ASSERT (n3 >= 0 && n3 <= 3);
return (n3 == 0)? hawk_rtx_makeintval(rtx,(hawk_int_t)l1+(hawk_int_t)l2):
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1+(hawk_flt_t)l2):
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1+(hawk_flt_t)r2):
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1+(hawk_flt_t)r2);
}
static hawk_val_t* eval_binop_minus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
HAWK_ASSERT (n3 >= 0 && n3 <= 3);
return (n3 == 0)? hawk_rtx_makeintval(rtx,(hawk_int_t)l1-(hawk_int_t)l2):
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1-(hawk_flt_t)l2):
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1-(hawk_flt_t)r2):
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1-(hawk_flt_t)r2);
}
static hawk_val_t* eval_binop_mul (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
n1 = hawk_rtx_valtonum (rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum (rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
HAWK_ASSERT (n3 >= 0 && n3 <= 3);
return (n3 == 0)? hawk_rtx_makeintval(rtx,(hawk_int_t)l1*(hawk_int_t)l2):
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1*(hawk_flt_t)l2):
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1*(hawk_flt_t)r2):
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1*(hawk_flt_t)r2);
}
static hawk_val_t* eval_binop_div (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
hawk_val_t* res;
n1 = hawk_rtx_valtonum (rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum (rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
switch (n3)
{
case 0:
if (l2 == 0)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EDIVBY0);
return HAWK_NULL;
}
if (((hawk_int_t)l1 % (hawk_int_t)l2) == 0)
{
res = hawk_rtx_makeintval (
rtx, (hawk_int_t)l1 / (hawk_int_t)l2);
}
else
{
res = hawk_rtx_makefltval (
rtx, (hawk_flt_t)l1 / (hawk_flt_t)l2);
}
break;
case 1:
res = hawk_rtx_makefltval (
rtx, (hawk_flt_t)r1 / (hawk_flt_t)l2);
break;
case 2:
res = hawk_rtx_makefltval (
rtx, (hawk_flt_t)l1 / (hawk_flt_t)r2);
break;
case 3:
res = hawk_rtx_makefltval (
rtx, (hawk_flt_t)r1 / (hawk_flt_t)r2);
break;
}
return res;
}
static hawk_val_t* eval_binop_idiv (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2, quo;
hawk_val_t* res;
n1 = hawk_rtx_valtonum (rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum (rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
switch (n3)
{
case 0:
if (l2 == 0)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EDIVBY0);
return HAWK_NULL;
}
res = hawk_rtx_makeintval (
rtx, (hawk_int_t)l1 / (hawk_int_t)l2);
break;
case 1:
quo = (hawk_flt_t)r1 / (hawk_flt_t)l2;
res = hawk_rtx_makeintval (rtx, (hawk_int_t)quo);
break;
case 2:
quo = (hawk_flt_t)l1 / (hawk_flt_t)r2;
res = hawk_rtx_makeintval (rtx, (hawk_int_t)quo);
break;
case 3:
quo = (hawk_flt_t)r1 / (hawk_flt_t)r2;
res = hawk_rtx_makeintval (rtx, (hawk_int_t)quo);
break;
}
return res;
}
static hawk_val_t* eval_binop_mod (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
hawk_val_t* res;
/* the mod function must be provided when the awk object is created to be able to calculate floating-pointer remainder */
HAWK_ASSERT (rtx->awk->prm.math.mod != HAWK_NULL);
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
switch (n3)
{
case 0:
if (l2 == 0)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EDIVBY0);
return HAWK_NULL;
}
res = hawk_rtx_makeintval(rtx, (hawk_int_t)l1 % (hawk_int_t)l2);
break;
case 1:
res = hawk_rtx_makefltval(rtx, rtx->awk->prm.math.mod(hawk_rtx_gethawk(rtx), (hawk_flt_t)r1, (hawk_flt_t)l2));
break;
case 2:
res = hawk_rtx_makefltval(rtx, rtx->awk->prm.math.mod(hawk_rtx_gethawk(rtx), (hawk_flt_t)l1, (hawk_flt_t)r2));
break;
case 3:
res = hawk_rtx_makefltval(rtx, rtx->awk->prm.math.mod(hawk_rtx_gethawk(rtx), (hawk_flt_t)r1, (hawk_flt_t)r2));
break;
}
return res;
}
static hawk_val_t* eval_binop_exp (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
int n1, n2, n3;
hawk_int_t l1, l2;
hawk_flt_t r1, r2;
hawk_val_t* res;
n1 = hawk_rtx_valtonum (rtx, left, &l1, &r1);
n2 = hawk_rtx_valtonum (rtx, right, &l2, &r2);
if (n1 <= -1 || n2 <= -1)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EOPERAND);
return HAWK_NULL;
}
n3 = n1 + (n2 << 1);
switch (n3)
{
case 0:
/* left - int, right - int */
if (l2 >= 0)
{
hawk_int_t v = 1;
while (l2-- > 0) v *= l1;
res = hawk_rtx_makeintval (rtx, v);
}
else if (l1 == 0)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EDIVBY0);
return HAWK_NULL;
}
else
{
hawk_flt_t v = 1.0;
l2 *= -1;
while (l2-- > 0) v /= l1;
res = hawk_rtx_makefltval (rtx, v);
}
break;
case 1:
/* left - real, right - int */
if (l2 >= 0)
{
hawk_flt_t v = 1.0;
while (l2-- > 0) v *= r1;
res = hawk_rtx_makefltval (rtx, v);
}
else if (r1 == 0.0)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EDIVBY0);
return HAWK_NULL;
}
else
{
hawk_flt_t v = 1.0;
l2 *= -1;
while (l2-- > 0) v /= r1;
res = hawk_rtx_makefltval (rtx, v);
}
break;
case 2:
/* left - int, right - real */
res = hawk_rtx_makefltval (
rtx,
rtx->awk->prm.math.pow(hawk_rtx_gethawk(rtx), (hawk_flt_t)l1, (hawk_flt_t)r2)
);
break;
case 3:
/* left - real, right - real */
res = hawk_rtx_makefltval (
rtx,
rtx->awk->prm.math.pow(hawk_rtx_gethawk(rtx), (hawk_flt_t)r1,(hawk_flt_t)r2)
);
break;
}
return res;
}
static hawk_val_t* eval_binop_concat (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
{
hawk_val_t* res;
hawk_rtx_valtostr_out_t lout, rout;
lout.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, left, &lout) <= -1) return HAWK_NULL;
rout.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, right, &rout) <= -1)
{
hawk_rtx_freemem (rtx, lout.u.cpldup.ptr);
return HAWK_NULL;
}
res = hawk_rtx_makestrvalwithoochars2(
rtx,
lout.u.cpldup.ptr, lout.u.cpldup.len,
rout.u.cpldup.ptr, rout.u.cpldup.len
);
hawk_rtx_freemem (rtx, rout.u.cpldup.ptr);
hawk_rtx_freemem (rtx, lout.u.cpldup.ptr);
return res;
}
static hawk_val_t* eval_binop_match0 (
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right,
const hawk_loc_t* lloc, const hawk_loc_t* rloc, int ret)
{
hawk_val_t* res;
hawk_oocs_t out;
int n;
out.ptr = hawk_rtx_getvaloocstr (rtx, left, &out.len);
if (out.ptr == HAWK_NULL) return HAWK_NULL;
n = hawk_rtx_matchval(rtx, right, &out, &out, HAWK_NULL, HAWK_NULL);
hawk_rtx_freevaloocstr (rtx, left, out.ptr);
if (n <= -1)
{
ADJERR_LOC (rtx, lloc);
return HAWK_NULL;
}
res = hawk_rtx_makeintval (rtx, (n == ret));
if (res == HAWK_NULL)
{
ADJERR_LOC (rtx, lloc);
return HAWK_NULL;
}
return res;
}
static hawk_val_t* eval_binop_ma (hawk_rtx_t* run, hawk_nde_t* left, hawk_nde_t* right)
{
hawk_val_t* lv, * rv, * res;
HAWK_ASSERT (left->next == HAWK_NULL);
HAWK_ASSERT (right->next == HAWK_NULL);
lv = eval_expression (run, left);
if (lv == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, lv);
rv = eval_expression0 (run, right);
if (rv == HAWK_NULL)
{
hawk_rtx_refdownval (run, lv);
return HAWK_NULL;
}
hawk_rtx_refupval (run, rv);
res = eval_binop_match0 (run, lv, rv, &left->loc, &right->loc, 1);
hawk_rtx_refdownval (run, rv);
hawk_rtx_refdownval (run, lv);
return res;
}
static hawk_val_t* eval_binop_nm (hawk_rtx_t* run, hawk_nde_t* left, hawk_nde_t* right)
{
hawk_val_t* lv, * rv, * res;
HAWK_ASSERT (left->next == HAWK_NULL);
HAWK_ASSERT (right->next == HAWK_NULL);
lv = eval_expression (run, left);
if (lv == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, lv);
rv = eval_expression0 (run, right);
if (rv == HAWK_NULL)
{
hawk_rtx_refdownval (run, lv);
return HAWK_NULL;
}
hawk_rtx_refupval (run, rv);
res = eval_binop_match0 (run, lv, rv, &left->loc, &right->loc, 0);
hawk_rtx_refdownval (run, rv);
hawk_rtx_refdownval (run, lv);
return res;
}
static hawk_val_t* eval_unary (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* left, * res = HAWK_NULL;
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
int n;
hawk_int_t l;
hawk_flt_t r;
HAWK_ASSERT (
exp->type == HAWK_NDE_EXP_UNR);
HAWK_ASSERT (
exp->left != HAWK_NULL && exp->right == HAWK_NULL);
HAWK_ASSERT (
exp->opcode == HAWK_UNROP_PLUS ||
exp->opcode == HAWK_UNROP_MINUS ||
exp->opcode == HAWK_UNROP_LNOT ||
exp->opcode == HAWK_UNROP_BNOT);
HAWK_ASSERT (exp->left->next == HAWK_NULL);
left = eval_expression (rtx, exp->left);
if (left == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, left);
switch (exp->opcode)
{
case HAWK_UNROP_MINUS:
n = hawk_rtx_valtonum (rtx, left, &l, &r);
if (n <= -1) goto exit_func;
res = (n == 0)? hawk_rtx_makeintval (rtx, -l):
hawk_rtx_makefltval (rtx, -r);
break;
case HAWK_UNROP_LNOT:
if (HAWK_RTX_GETVALTYPE (rtx, left) == HAWK_VAL_STR)
{
/* 0 if the string length is greater than 0.
* 1 if it's empty */
res = hawk_rtx_makeintval (
rtx, !(((hawk_val_str_t*)left)->val.len > 0));
}
else
{
n = hawk_rtx_valtonum (rtx, left, &l, &r);
if (n <= -1) goto exit_func;
res = (n == 0)? hawk_rtx_makeintval (rtx, !l):
hawk_rtx_makefltval (rtx, !r);
}
break;
case HAWK_UNROP_BNOT:
n = hawk_rtx_valtoint (rtx, left, &l);
if (n <= -1) goto exit_func;
res = hawk_rtx_makeintval (rtx, ~l);
break;
case HAWK_UNROP_PLUS:
n = hawk_rtx_valtonum (rtx, left, &l, &r);
if (n <= -1) goto exit_func;
res = (n == 0)? hawk_rtx_makeintval (rtx, l):
hawk_rtx_makefltval (rtx, r);
break;
}
exit_func:
hawk_rtx_refdownval (rtx, left);
if (res == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
return res;
}
static hawk_val_t* eval_incpre (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* left, * res;
hawk_val_type_t left_vtype;
hawk_int_t inc_val_int;
hawk_flt_t inc_val_flt;
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
HAWK_ASSERT (exp->type == HAWK_NDE_EXP_INCPRE);
HAWK_ASSERT (exp->left != HAWK_NULL && exp->right == HAWK_NULL);
/* this way of checking if the l-value is assignable is
* ugly as it is dependent on the node types defined in hawk.h
* but let's keep going this way for the time being. */
if (exp->left->type < HAWK_NDE_NAMED ||
/*exp->left->type > HAWK_NDE_ARGIDX) XXX */
exp->left->type > HAWK_NDE_POS)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EOPERAND);
return HAWK_NULL;
}
if (exp->opcode == HAWK_INCOP_PLUS)
{
inc_val_int = 1;
inc_val_flt = 1.0;
}
else if (exp->opcode == HAWK_INCOP_MINUS)
{
inc_val_int = -1;
inc_val_flt = -1.0;
}
else
{
HAWK_ASSERT (!"should never happen - invalid opcode");
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EINTERN);
return HAWK_NULL;
}
HAWK_ASSERT (exp->left->next == HAWK_NULL);
left = eval_expression (rtx, exp->left);
if (left == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, left);
left_vtype = HAWK_RTX_GETVALTYPE (rtx, left);
switch (left_vtype)
{
case HAWK_VAL_INT:
{
hawk_int_t r = HAWK_RTX_GETINTFROMVAL (rtx, left);
res = hawk_rtx_makeintval (rtx, r + inc_val_int);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
break;
}
case HAWK_VAL_FLT:
{
hawk_flt_t r = ((hawk_val_flt_t*)left)->val;
res = hawk_rtx_makefltval (rtx, r + inc_val_flt);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
break;
}
default:
{
hawk_int_t v1;
hawk_flt_t v2;
int n;
n = hawk_rtx_valtonum (rtx, left, &v1, &v2);
if (n <= -1)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
if (n == 0)
{
res = hawk_rtx_makeintval (rtx, v1 + inc_val_int);
}
else /* if (n == 1) */
{
HAWK_ASSERT (n == 1);
res = hawk_rtx_makefltval (rtx, v2 + inc_val_flt);
}
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
break;
}
}
if (do_assignment (rtx, exp->left, res) == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, left);
return res;
}
static hawk_val_t* eval_incpst (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* left, * res, * res2;
hawk_val_type_t left_vtype;
hawk_int_t inc_val_int;
hawk_flt_t inc_val_flt;
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
HAWK_ASSERT (exp->type == HAWK_NDE_EXP_INCPST);
HAWK_ASSERT (exp->left != HAWK_NULL && exp->right == HAWK_NULL);
/* this way of checking if the l-value is assignable is
* ugly as it is dependent on the node types defined in hawk.h.
* but let's keep going this way for the time being. */
if (exp->left->type < HAWK_NDE_NAMED ||
/*exp->left->type > HAWK_NDE_ARGIDX) XXX */
exp->left->type > HAWK_NDE_POS)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EOPERAND);
return HAWK_NULL;
}
if (exp->opcode == HAWK_INCOP_PLUS)
{
inc_val_int = 1;
inc_val_flt = 1.0;
}
else if (exp->opcode == HAWK_INCOP_MINUS)
{
inc_val_int = -1;
inc_val_flt = -1.0;
}
else
{
HAWK_ASSERT (!"should never happen - invalid opcode");
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EINTERN);
return HAWK_NULL;
}
HAWK_ASSERT (exp->left->next == HAWK_NULL);
left = eval_expression (rtx, exp->left);
if (left == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, left);
left_vtype = HAWK_RTX_GETVALTYPE (rtx, left);
switch (left_vtype)
{
case HAWK_VAL_INT:
{
hawk_int_t r = HAWK_RTX_GETINTFROMVAL (rtx, left);
res = hawk_rtx_makeintval (rtx, r);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
res2 = hawk_rtx_makeintval (rtx, r + inc_val_int);
if (res2 == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
hawk_rtx_freeval (rtx, res, 1);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
break;
}
case HAWK_VAL_FLT:
{
hawk_flt_t r = ((hawk_val_flt_t*)left)->val;
res = hawk_rtx_makefltval (rtx, r);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
res2 = hawk_rtx_makefltval (rtx, r + inc_val_flt);
if (res2 == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
hawk_rtx_freeval (rtx, res, 1);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
break;
}
default:
{
hawk_int_t v1;
hawk_flt_t v2;
int n;
n = hawk_rtx_valtonum (rtx, left, &v1, &v2);
if (n <= -1)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
if (n == 0)
{
res = hawk_rtx_makeintval (rtx, v1);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
res2 = hawk_rtx_makeintval (rtx, v1 + inc_val_int);
if (res2 == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
hawk_rtx_freeval (rtx, res, 1);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
}
else /* if (n == 1) */
{
HAWK_ASSERT (n == 1);
res = hawk_rtx_makefltval (rtx, v2);
if (res == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
res2 = hawk_rtx_makefltval (rtx, v2 + inc_val_flt);
if (res2 == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
hawk_rtx_freeval (rtx, res, 1);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
}
break;
}
}
if (do_assignment (rtx, exp->left, res2) == HAWK_NULL)
{
hawk_rtx_refdownval (rtx, left);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, left);
return res;
}
static hawk_val_t* eval_cnd (hawk_rtx_t* run, hawk_nde_t* nde)
{
hawk_val_t* tv, * v;
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)nde;
HAWK_ASSERT (cnd->test->next == HAWK_NULL);
tv = eval_expression (run, cnd->test);
if (tv == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (run, tv);
HAWK_ASSERT (cnd->left->next == HAWK_NULL && cnd->right->next == HAWK_NULL);
v = (hawk_rtx_valtobool(run, tv))? eval_expression(run, cnd->left): eval_expression(run, cnd->right);
hawk_rtx_refdownval (run, tv);
return v;
}
static hawk_val_t* eval_fncall_fnc (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
/* intrinsic function */
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
/* the parser must make sure that the number of arguments is proper */
HAWK_ASSERT (call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
return __eval_call(rtx, nde, HAWK_NULL, push_arg_from_nde, (void*)call->u.fnc.spec.arg.spec, HAWK_NULL, HAWK_NULL);
}
static HAWK_INLINE hawk_val_t* eval_fncall_fun_ex (hawk_rtx_t* rtx, hawk_nde_t* nde, void(*errhandler)(void*), void* eharg)
{
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
hawk_fun_t* fun;
hawk_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 = hawk_htb_search(rtx->awk->tree.funs, call->u.fun.name.ptr, call->u.fun.name.len);
if (!pair)
{
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EFUNNF, HAWK_T("function '%.*js' not found"), call->u.fun.name.len, call->u.fun.name.ptr);
return HAWK_NULL;
}
fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair);
HAWK_ASSERT (fun != HAWK_NULL);
/* 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? */
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EARGTM);
return HAWK_NULL;
}
return __eval_call(rtx, nde, fun, push_arg_from_nde, HAWK_NULL, errhandler, eharg);
}
static hawk_val_t* eval_fncall_fun (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return eval_fncall_fun_ex(rtx, nde, HAWK_NULL, HAWK_NULL);
}
static hawk_val_t* eval_fncall_var (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
hawk_val_t* fv, * rv;
fv = eval_expression(rtx, (hawk_nde_t*)call->u.var.var);
if (!fv) return HAWK_NULL;
hawk_rtx_refupval (rtx, fv);
if (HAWK_RTX_GETVALTYPE(rtx, fv) != HAWK_VAL_FUN)
{
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_ENOTFUN, HAWK_T("non-function value in %.*js"), call->u.var.var->id.name.len, call->u.var.var->id.name.ptr);
rv = HAWK_NULL;
}
else
{
hawk_fun_t* fun = ((hawk_val_fun_t*)fv)->fun;
rv = __eval_call(rtx, nde, fun, push_arg_from_nde, HAWK_NULL, HAWK_NULL, HAWK_NULL);
}
hawk_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
* hawk_rtx_refdownval (run, RTX_STACK_ARG(run,nargs));*/
#define UNWIND_RTX_STACK_ARG(rtx,nargs) \
do { \
while ((nargs) > 0) \
{ \
--(nargs); \
hawk_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 hawk_val_t* __eval_call (
hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_fun_t* fun,
hawk_oow_t(*argpusher)(hawk_rtx_t*,hawk_nde_fncall_t*,void*), void* apdata,
void(*errhandler)(void*), void* eharg)
{
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
hawk_oow_t saved_stack_top;
hawk_oow_t nargs, i;
hawk_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
* ---------------------
*/
HAWK_ASSERT (HAWK_SIZEOF(void*) >= HAWK_SIZEOF(rtx->stack_top));
HAWK_ASSERT (HAWK_SIZEOF(void*) >= HAWK_SIZEOF(rtx->stack_base));
saved_stack_top = rtx->stack_top;
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("setting up function stack frame top=%zd base=%zd\n"), (hawk_oow_t)rtx->stack_top, (hawk_oow_t)rtx->stack_base);
#endif
if (__raw_push(rtx,(void*)rtx->stack_base) <= -1)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOMEM);
return HAWK_NULL;
}
if (__raw_push(rtx,(void*)saved_stack_top) <= -1)
{
__raw_pop (rtx);
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOMEM);
return HAWK_NULL;
}
/* secure space for a return value. */
if (__raw_push(rtx,hawk_val_nil) <= -1)
{
__raw_pop (rtx);
__raw_pop (rtx);
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOMEM);
return HAWK_NULL;
}
/* secure space for nargs */
if (__raw_push(rtx,hawk_val_nil) <= -1)
{
__raw_pop (rtx);
__raw_pop (rtx);
__raw_pop (rtx);
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOMEM);
return HAWK_NULL;
}
/* push all arguments onto the stack */
nargs = argpusher(rtx, call, apdata);
if (nargs == (hawk_oow_t)-1)
{
UNWIND_RTX_STACK_BASE (rtx);
return HAWK_NULL;
}
HAWK_ASSERT (nargs == call->nargs);
if (fun)
{
/* extra step for normal awk functions */
if (fun->argspec && call->args) /* hawk_rtx_callfun() sets up a fake call structure with nargs > 0 but args == HAWK_NULL */
{
/* sanity check for pass-by-reference parameters of a normal awk function.
* it tests if each pass-by-reference argument is referenceable. */
hawk_nde_t* p = call->args;
for (i = 0; i < nargs; i++)
{
if (fun->argspec[i] == HAWK_T('r'))
{
hawk_val_t** ref;
if (get_reference(rtx, p, &ref) <= -1)
{
UNWIND_RTX_STACK (rtx, nargs);
return HAWK_NULL;
}
}
p = p->next;
}
}
while (nargs < fun->nargs)
{
/* push as many nils as the number of missing actual arguments */
if (__raw_push(rtx, hawk_val_nil) <= -1)
{
UNWIND_RTX_STACK (rtx, nargs);
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOMEM);
return HAWK_NULL;
}
nargs++;
}
}
rtx->stack_base = saved_stack_top;
RTX_STACK_NARGS(rtx) = (void*)nargs;
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("running function body\n"));
#endif
if (fun)
{
/* normal awk function */
HAWK_ASSERT (fun->body->type == HAWK_NDE_BLK);
n = run_block(rtx,(hawk_nde_blk_t*)fun->body);
}
else
{
n = 0;
/* intrinsic function */
HAWK_ASSERT (call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
if (call->u.fnc.spec.impl)
{
n = call->u.fnc.spec.impl(rtx, &call->u.fnc.info);
if (n <= -1)
{
ADJERR_LOC (rtx, &nde->loc);
/* correct the return code just in case */
if (n < -1) n = -1;
}
}
}
/* refdown args in the rtx.stack */
nargs = (hawk_oow_t)RTX_STACK_NARGS(rtx);
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("block rtx complete nargs = %d\n"), (int)nargs);
#endif
if (fun && fun->argspec && call->args && call->nargs > 0) /* hawk_rtx_callfun() sets up a fake call structure with nargs > 0 but args == HAWK_NULL */
{
/* set back the values for pass-by-reference parameters of normal functions.
* the intrinsic functions are not handled here but their implementation would
* call hawk_rtx_setrefval() */
/* even if fun->argspec exists, call->nargs may still be 0. so i test if call->nargs > 0.
* function x(a1, &a2) {}
* BEGIN { x(); }
* all parameters are nils in this case. nargs and fun->nargs are 2 but call->nargs is 0.
*/
hawk_oow_t cur_stack_base = rtx->stack_base;
hawk_oow_t prev_stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
hawk_nde_t* p = call->args;
for (i = 0; i < call->nargs; i++)
{
if (fun->argspec[i] == HAWK_T('r'))
{
hawk_val_t** ref;
hawk_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 */
HAWK_RTX_INIT_REF_VAL (&refv, p->type - HAWK_NDE_NAMED, ref, 9); /* initialize a fake reference variable. 9 chosen randomly */
hawk_rtx_setrefval (rtx, &refv, RTX_STACK_ARG(rtx, i));
}
hawk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
p = p->next;
}
for (; i < nargs; i++)
{
hawk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
}
}
else
{
for (i = 0; i < nargs; i++)
{
hawk_rtx_refdownval (rtx, RTX_STACK_ARG(rtx,i));
}
}
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("got return value\n"));
#endif
v = RTX_STACK_RETVAL(rtx);
if (n == -1)
{
if (hawk_rtx_geterrnum(rtx) == HAWK_ENOERR && errhandler != HAWK_NULL)
{
/* errhandler is passed only when __eval_call() is
* invoked from hawk_rtx_call(). Under this
* circumstance, this stack frame is the first
* activated and the stack base is the first element
* after the global variables. so RTX_STACK_RETVAL(rtx)
* effectively becomes RTX_STACK_RETVAL_GBL(rtx).
* As __eval_call() returns HAWK_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 HAWK_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 */
hawk_rtx_refdownval (rtx, v);
RTX_STACK_RETVAL(rtx) = hawk_val_nil;
}
else
{
/* this trick has been mentioned in rtx_return.
* adjust the reference count of the return value.
* the value must not be freed even if the reference count
* reached zero because its reference has been incremented
* in rtx_return or directly by hawk_rtx_setretval()
* regardless of its reference count. */
hawk_rtx_refdownval_nofree (rtx, v);
}
rtx->stack_top = (hawk_oow_t)rtx->stack[rtx->stack_base + 1];
rtx->stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
if (rtx->exit_level == EXIT_FUNCTION) rtx->exit_level = EXIT_NONE;
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("returning from function top=%zd, base=%zd\n"), (hawk_oow_t)rtx->stack_top, (hawk_oow_t)rtx->stack_base);
#endif
return (n == -1)? HAWK_NULL: v;
}
static hawk_oow_t push_arg_from_vals (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, void* data)
{
struct pafv_t* pafv = (struct pafv_t*)data;
hawk_oow_t nargs = 0;
for (nargs = 0; nargs < pafv->nargs; nargs++)
{
if (pafv->argspec && pafv->argspec[nargs] == HAWK_T('r'))
{
hawk_val_t** ref;
hawk_val_t* v;
ref = (hawk_val_t**)&pafv->args[nargs];
v = hawk_rtx_makerefval(rtx, HAWK_VAL_REF_LCL, ref); /* this type(HAWK_VAL_REF_LCL) is fake */
if (!v)
{
UNWIND_RTX_STACK_ARG (rtx, nargs);
ADJERR_LOC (rtx, &call->loc);
return (hawk_oow_t)-1;
}
if (__raw_push(rtx, v) <= -1)
{
hawk_rtx_refupval (rtx, v);
hawk_rtx_refdownval (rtx, v);
UNWIND_RTX_STACK_ARG (rtx, nargs);
hawk_rtx_seterrnum (rtx, &call->loc, HAWK_ENOMEM);
return (hawk_oow_t)-1;
}
hawk_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 */
hawk_rtx_refupval (rtx, pafv->args[nargs]);
hawk_rtx_refdownval (rtx, pafv->args[nargs]);
UNWIND_RTX_STACK_ARG (rtx, nargs);
hawk_rtx_seterrnum (rtx, &call->loc, HAWK_ENOMEM);
return (hawk_oow_t)-1;
}
hawk_rtx_refupval (rtx, pafv->args[nargs]);
}
}
return nargs;
}
static hawk_oow_t push_arg_from_nde (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, void* data)
{
hawk_nde_t* p;
hawk_val_t* v;
hawk_oow_t nargs;
const hawk_ooch_t* fnc_arg_spec = (const hawk_ooch_t*)data;
for (p = call->args, nargs = 0; p != HAWK_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] == HAWK_T('r'))
{
hawk_val_t** ref;
if (get_reference(rtx, p, &ref) <= -1)
{
UNWIND_RTX_STACK_ARG (rtx, nargs);
return (hawk_oow_t)-1;
}
/* 'p->type - HAWK_NDE_NAMED' must produce a relevant HAWK_VAL_REF_XXX value. */
v = hawk_rtx_makerefval(rtx, p->type - HAWK_NDE_NAMED, ref);
}
else if (fnc_arg_spec && fnc_arg_spec[nargs] == HAWK_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 (hawk_oow_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 */
hawk_rtx_refupval (rtx, v);
hawk_rtx_refdownval (rtx, v);
UNWIND_RTX_STACK_ARG (rtx, nargs);
hawk_rtx_seterrnum (rtx, &call->loc, HAWK_ENOMEM);
return (hawk_oow_t)-1;
}
hawk_rtx_refupval (rtx, v);
}
HAWK_ASSERT (call->nargs == nargs);
return nargs;
}
static int get_reference (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_val_t*** ref)
{
hawk_nde_var_t* tgt = (hawk_nde_var_t*)nde;
hawk_val_t** tmp;
/* refer to eval_indexed for application of a similar concept */
switch (nde->type)
{
case HAWK_NDE_NAMED:
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
if (pair == HAWK_NULL)
{
/* it is bad that the named variable has to be created here.
* would there be any better ways to avoid this? */
pair = hawk_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, hawk_val_nil, 0);
if (!pair)
{
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
}
*ref = (hawk_val_t**)&HAWK_HTB_VPTR(pair);
return 0;
}
case HAWK_NDE_GBL:
/* *ref = (hawk_val_t**)&RTX_STACK_GBL(rtx,tgt->id.idxa); */
*ref = (hawk_val_t**)((hawk_oow_t)tgt->id.idxa);
return 0;
case HAWK_NDE_LCL:
*ref = (hawk_val_t**)&RTX_STACK_LCL(rtx,tgt->id.idxa);
return 0;
case HAWK_NDE_ARG:
*ref = (hawk_val_t**)&RTX_STACK_ARG(rtx,tgt->id.idxa);
return 0;
case HAWK_NDE_NAMEDIDX:
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
if (pair == HAWK_NULL)
{
pair = hawk_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, hawk_val_nil, 0);
if (pair == HAWK_NULL)
{
ADJERR_LOC (rtx, &nde->loc);
return -1;
}
}
tmp = get_reference_indexed(rtx, tgt, (hawk_val_t**)&HAWK_HTB_VPTR(pair));
if (tmp == HAWK_NULL) return -1;
*ref = tmp;
return 0;
}
case HAWK_NDE_GBLIDX:
tmp = get_reference_indexed(rtx, tgt, (hawk_val_t**)&RTX_STACK_GBL(rtx,tgt->id.idxa));
if (tmp == HAWK_NULL) return -1;
*ref = tmp;
return 0;
case HAWK_NDE_LCLIDX:
tmp = get_reference_indexed(rtx, tgt, (hawk_val_t**)&RTX_STACK_LCL(rtx,tgt->id.idxa));
if (tmp == HAWK_NULL) return -1;
*ref = tmp;
return 0;
case HAWK_NDE_ARGIDX:
tmp = get_reference_indexed(rtx, tgt, (hawk_val_t**)&RTX_STACK_ARG(rtx,tgt->id.idxa));
if (tmp == HAWK_NULL) return -1;
*ref = tmp;
return 0;
case HAWK_NDE_POS:
{
int n;
hawk_int_t lv;
hawk_val_t* v;
/* the position number is returned for the positional
* variable unlike other reference types. */
v = eval_expression(rtx, ((hawk_nde_pos_t*)nde)->val);
if (v == HAWK_NULL) return -1;
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint(rtx, v, &lv);
hawk_rtx_refdownval (rtx, v);
if (n <= -1)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EPOSIDX);
return -1;
}
if (!IS_VALID_POSIDX(lv))
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EPOSIDX);
return -1;
}
*ref = (hawk_val_t**)((hawk_oow_t)lv);
return 0;
}
default:
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOTREF);
return -1;
}
}
static hawk_val_t** get_reference_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* nde, hawk_val_t** val)
{
hawk_htb_pair_t* pair;
hawk_ooch_t* str;
hawk_oow_t len;
hawk_ooch_t idxbuf[IDXBUFSIZE];
hawk_val_type_t vtype;
HAWK_ASSERT (val != HAWK_NULL);
vtype = HAWK_RTX_GETVALTYPE(rtx, *val);
if (vtype == HAWK_VAL_NIL)
{
hawk_val_t* tmp;
tmp = hawk_rtx_makemapval (rtx);
if (tmp == HAWK_NULL)
{
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, *val);
*val = tmp;
hawk_rtx_refupval (rtx, (hawk_val_t*)*val);
}
else if (vtype != HAWK_VAL_MAP)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOTMAP);
return HAWK_NULL;
}
HAWK_ASSERT (nde->idx != HAWK_NULL);
len = HAWK_COUNTOF(idxbuf);
str = idxnde_to_str (rtx, nde->idx, idxbuf, &len);
if (str == HAWK_NULL) return HAWK_NULL;
pair = hawk_htb_search((*(hawk_val_map_t**)val)->map, str, len);
if (pair == HAWK_NULL)
{
pair = hawk_htb_upsert((*(hawk_val_map_t**)val)->map, str, len, hawk_val_nil, 0);
if (pair == HAWK_NULL)
{
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, HAWK_HTB_VPTR(pair));
}
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
return (hawk_val_t**)&HAWK_HTB_VPTR(pair);
}
static hawk_val_t* eval_int (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
val = hawk_rtx_makeintval(rtx, ((hawk_nde_int_t*)nde)->val);
if (val == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
else if (HAWK_VTR_IS_POINTER(val)) ((hawk_val_int_t*)val)->nde = nde;
return val;
}
static hawk_val_t* eval_flt (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
val = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
if (val == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
else ((hawk_val_flt_t*)val)->nde = nde;
return val;
}
static hawk_val_t* eval_str (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
val = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
if (val == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
return val;
}
static hawk_val_t* eval_mbs (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
val = hawk_rtx_makembsval(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
if (val == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
return val;
}
static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
val = hawk_rtx_makerexval(rtx, &((hawk_nde_rex_t*)nde)->str, ((hawk_nde_rex_t*)nde)->code);
if (val == HAWK_NULL) ADJERR_LOC (rtx, &nde->loc);
return val;
}
static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_val_t* val;
hawk_fun_t* fun;
fun = ((hawk_nde_fun_t*)nde)->funptr;
if (!fun)
{
hawk_htb_pair_t* pair;
/* for a program like this,
BEGIN { @local x; abc(); x = abc; x(); } function abc() { print "abc"; }
* abc is defined after BEGIN but is know to be a function inside BEGIN.
* the funptr field in the node can be HAWK_NULL */
/* TODO: support builtin functions?, support module functions? */
pair = hawk_htb_search(rtx->awk->tree.funs, ((hawk_nde_fun_t*)nde)->name.ptr, ((hawk_nde_fun_t*)nde)->name.len);
if (!pair)
{
/* it's unlikely to be not found in the function table for the parser structure. but keep this code in case */
hawk_rtx_seterrfmt (rtx, &nde->loc, HAWK_EFUNNF, HAWK_T("function '%.*js' not found"), ((hawk_nde_fun_t*)nde)->name.len, ((hawk_nde_fun_t*)nde)->name.ptr);
return HAWK_NULL;
}
fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair);
HAWK_ASSERT (fun != HAWK_NULL);
}
val = hawk_rtx_makefunval(rtx, fun);
if (!val) ADJERR_LOC (rtx, &nde->loc);
return val;
}
static hawk_val_t* eval_named (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_htb_pair_t* pair;
pair = hawk_htb_search (rtx->named, ((hawk_nde_var_t*)nde)->id.name.ptr, ((hawk_nde_var_t*)nde)->id.name.len);
return (pair == HAWK_NULL)? hawk_val_nil: HAWK_HTB_VPTR(pair);
}
static hawk_val_t* eval_gbl (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return RTX_STACK_GBL(rtx,((hawk_nde_var_t*)nde)->id.idxa);
}
static hawk_val_t* eval_lcl (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return RTX_STACK_LCL(rtx,((hawk_nde_var_t*)nde)->id.idxa);
}
static hawk_val_t* eval_arg (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return RTX_STACK_ARG(rtx,((hawk_nde_var_t*)nde)->id.idxa);
}
static hawk_val_t* eval_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* nde, hawk_val_t** val)
{
hawk_htb_pair_t* pair;
hawk_ooch_t* str;
hawk_oow_t len;
hawk_ooch_t idxbuf[IDXBUFSIZE];
hawk_val_type_t vtype;
HAWK_ASSERT (val != HAWK_NULL);
vtype = HAWK_RTX_GETVALTYPE(rtx, *val);
if (vtype == HAWK_VAL_NIL)
{
hawk_val_t* tmp;
tmp = hawk_rtx_makemapval(rtx);
if (tmp == HAWK_NULL)
{
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, *val);
*val = tmp;
hawk_rtx_refupval (rtx, (hawk_val_t*)*val);
}
else if (vtype != HAWK_VAL_MAP)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_ENOTMAP);
return HAWK_NULL;
}
HAWK_ASSERT (nde->idx != HAWK_NULL);
len = HAWK_COUNTOF(idxbuf);
str = idxnde_to_str(rtx, nde->idx, idxbuf, &len);
if (str == HAWK_NULL) return HAWK_NULL;
pair = hawk_htb_search((*(hawk_val_map_t**)val)->map, str, len);
if (str != idxbuf) hawk_rtx_freemem (rtx, str);
return (pair == HAWK_NULL)? hawk_val_nil: (hawk_val_t*)HAWK_HTB_VPTR(pair);
}
static hawk_val_t* eval_namedidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_nde_var_t* tgt = (hawk_nde_var_t*)nde;
hawk_htb_pair_t* pair;
pair = hawk_htb_search(rtx->named, tgt->id.name.ptr, tgt->id.name.len);
if (pair == HAWK_NULL)
{
pair = hawk_htb_upsert(rtx->named, tgt->id.name.ptr, tgt->id.name.len, hawk_val_nil, 0);
if (pair == HAWK_NULL)
{
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, HAWK_HTB_VPTR(pair));
}
return eval_indexed (rtx, tgt, (hawk_val_t**)&HAWK_HTB_VPTR(pair));
}
static hawk_val_t* eval_gblidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return eval_indexed(rtx, (hawk_nde_var_t*)nde, (hawk_val_t**)&RTX_STACK_GBL(rtx,((hawk_nde_var_t*)nde)->id.idxa));
}
static hawk_val_t* eval_lclidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return eval_indexed(rtx, (hawk_nde_var_t*)nde, (hawk_val_t**)&RTX_STACK_LCL(rtx,((hawk_nde_var_t*)nde)->id.idxa));
}
static hawk_val_t* eval_argidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return eval_indexed(rtx, (hawk_nde_var_t*)nde, (hawk_val_t**)&RTX_STACK_ARG(rtx,((hawk_nde_var_t*)nde)->id.idxa));
}
static hawk_val_t* eval_pos (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
hawk_nde_pos_t* pos = (hawk_nde_pos_t*)nde;
hawk_val_t* v;
hawk_int_t lv;
int n;
v = eval_expression (rtx, pos->val);
if (v == HAWK_NULL) return HAWK_NULL;
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint (rtx, v, &lv);
hawk_rtx_refdownval (rtx, v);
if (n <= -1)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EPOSIDX);
return HAWK_NULL;
}
if (lv < 0)
{
hawk_rtx_seterrnum (rtx, &nde->loc, HAWK_EPOSIDX);
return HAWK_NULL;
}
if (lv == 0) v = rtx->inrec.d0;
else if (lv > 0 && lv <= (hawk_int_t)rtx->inrec.nflds)
v = rtx->inrec.flds[lv-1].val;
else v = hawk_val_zls; /*hawk_val_nil;*/
return v;
}
static hawk_val_t* __eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde, int mbs)
{
hawk_nde_getline_t* p;
hawk_val_t* v, * tmp;
hawk_ooch_t* dst;
hawk_ooecs_t* buf;
int n, x;
hawk_val_type_t vtype;
p = (hawk_nde_getline_t*)nde;
HAWK_ASSERT (
(p->in_type == HAWK_IN_PIPE && p->in != HAWK_NULL) ||
(p->in_type == HAWK_IN_RWPIPE && p->in != HAWK_NULL) ||
(p->in_type == HAWK_IN_FILE && p->in != HAWK_NULL) ||
(p->in_type == HAWK_IN_CONSOLE && p->in == HAWK_NULL));
if (p->in)
{
hawk_oow_t len;
hawk_rtx_valtostr_out_t out;
v = eval_expression(rtx, p->in);
if (!v) return HAWK_NULL;
hawk_rtx_refupval (rtx, v);
dst = hawk_rtx_getvaloocstr(rtx, v, &len);
if (!dst)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
if (len <= 0)
{
hawk_rtx_freevaloocstr (rtx, v, dst);
hawk_rtx_refdownval (rtx, v);
n = -1;
goto skip_read;
}
while (len > 0)
{
if (dst[--len] == HAWK_T('\0'))
{
/* the input source name contains a null
* character. make getline return -1.
* unlike print & printf, it is not a hard
* error */
hawk_rtx_freevaloocstr (rtx, v, dst);
hawk_rtx_refdownval (rtx, v);
n = -1;
goto skip_read;
}
}
}
else dst = (hawk_ooch_t*)HAWK_T("");
buf = &rtx->inrec.lineg;
read_console_again:
hawk_ooecs_clear (&rtx->inrec.lineg);
n = hawk_rtx_readio(rtx, p->in_type, dst, buf);
if (p->in)
{
hawk_rtx_freevaloocstr (rtx, v, dst);
hawk_rtx_refdownval (rtx, v);
}
if (n <= -1)
{
/* make getline return -1 */
n = -1;
}
else if (n > 0)
{
if (p->in_type == HAWK_IN_CONSOLE)
{
HAWK_ASSERT (p->in == HAWK_NULL);
if (rtx->nrflt.limit > 0)
{
/* record filter based on record number(NR) */
if (((rtx->gbl.nr / rtx->nrflt.limit) % rtx->nrflt.size) != rtx->nrflt.rank)
{
if (update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return HAWK_NULL;
/* this jump is a bit dirty. the 'if' block below hawk_rtx_readio()
* will never be true. but this makes code confusing */
goto read_console_again;
}
}
}
if (p->var == HAWK_NULL)
{
/* set $0 with the input value */
x = hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(buf));
if (x <= -1) return HAWK_NULL;
}
else
{
hawk_val_t* v;
v = hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(buf));
if (v == HAWK_NULL)
{
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, v);
tmp = do_assignment(rtx, p->var, v);
hawk_rtx_refdownval (rtx, v);
if (tmp == HAWK_NULL) return HAWK_NULL;
}
/* update FNR & NR if reading from console */
if (p->in_type == HAWK_IN_CONSOLE &&
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1)
{
return HAWK_NULL;
}
}
skip_read:
tmp = hawk_rtx_makeintval(rtx, n);
if (!tmp) ADJERR_LOC (rtx, &nde->loc);
return tmp;
}
static hawk_val_t* eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde)
{
return __eval_getline(rtx, nde, 0);
}
static hawk_val_t* eval_print (hawk_rtx_t* run, hawk_nde_t* nde)
{
int n = run_print(run, (hawk_nde_print_t*)nde);
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
else if (n <= -1) return HAWK_NULL;
return hawk_rtx_makeintval(run, n);
}
static hawk_val_t* eval_printf (hawk_rtx_t* run, hawk_nde_t* nde)
{
int n = run_printf(run, (hawk_nde_print_t*)nde);
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
else if (n <= -1) return HAWK_NULL;
return hawk_rtx_makeintval(run, n);
}
static int __raw_push (hawk_rtx_t* rtx, void* val)
{
if (rtx->stack_top >= rtx->stack_limit)
{
/*
void** tmp;
hawk_oow_t n;
n = rtx->stack_limit + RTX_STACK_INCREMENT;
tmp = (void**)hawk_rtx_reallocmem(rtx, rtx->stack, n * HAWK_SIZEOF(void*));
if (!tmp) return -1;
rtx->stack = tmp;
rtx->stack_limit = n;
*/
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_ESTACK, HAWK_T("runtime stack full"));
return -1;
}
rtx->stack[rtx->stack_top++] = val;
return 0;
}
static int read_record (hawk_rtx_t* rtx)
{
hawk_ooi_t n;
hawk_ooecs_t* buf;
read_again:
if (hawk_rtx_clrrec (rtx, 0) == -1) return -1;
buf = &rtx->inrec.line;
n = hawk_rtx_readio (rtx, HAWK_IN_CONSOLE, HAWK_T(""), buf);
if (n <= -1)
{
hawk_rtx_clrrec (rtx, 0);
return -1;
}
#if defined(DEBUG_RUN)
hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("record len = %d str=[%.*js]\n"), (int)HAWK_OOECS_LEN(buf), HAWK_OOECS_LEN(buf), HAWK_OOECS_PTR(buf));
#endif
if (n == 0)
{
HAWK_ASSERT (HAWK_OOECS_LEN(buf) == 0);
return 0;
}
if (rtx->nrflt.limit > 0)
{
if (((rtx->gbl.nr / rtx->nrflt.limit) % rtx->nrflt.size) != rtx->nrflt.rank)
{
if (update_fnr (rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return -1;
goto read_again;
}
}
if (hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(buf)) <= -1 ||
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return -1;
return 1;
}
static int shorten_record (hawk_rtx_t* rtx, hawk_oow_t nflds)
{
hawk_val_t* v;
hawk_ooch_t* ofs_free = HAWK_NULL, * ofs_ptr;
hawk_oow_t ofs_len, i;
hawk_ooecs_t tmp;
hawk_val_type_t vtype;
HAWK_ASSERT (nflds <= rtx->inrec.nflds);
if (nflds > 1)
{
v = RTX_STACK_GBL(rtx, HAWK_GBL_OFS);
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
if (vtype == HAWK_VAL_NIL)
{
/* OFS not set */
ofs_ptr = HAWK_T(" ");
ofs_len = 1;
}
else if (vtype == HAWK_VAL_STR)
{
ofs_ptr = ((hawk_val_str_t*)v)->val.ptr;
ofs_len = ((hawk_val_str_t*)v)->val.len;
}
else
{
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr (rtx, v, &out) <= -1) return -1;
ofs_ptr = out.u.cpldup.ptr;
ofs_len = out.u.cpldup.len;
ofs_free = ofs_ptr;
}
}
if (hawk_ooecs_init(&tmp, hawk_rtx_getgem(rtx), HAWK_OOECS_LEN(&rtx->inrec.line)) <= -1)
{
if (ofs_free) hawk_rtx_freemem (rtx, ofs_free);
if (nflds > 1) hawk_rtx_refdownval (rtx, v);
return -1;
}
for (i = 0; i < nflds; i++)
{
if (i > 0 && hawk_ooecs_ncat(&tmp,ofs_ptr,ofs_len) == (hawk_oow_t)-1)
{
hawk_ooecs_fini (&tmp);
if (ofs_free) hawk_rtx_freemem (rtx, ofs_free);
if (nflds > 1) hawk_rtx_refdownval (rtx, v);
return -1;
}
if (hawk_ooecs_ncat(&tmp, rtx->inrec.flds[i].ptr, rtx->inrec.flds[i].len) == (hawk_oow_t)-1)
{
hawk_ooecs_fini (&tmp);
if (ofs_free) hawk_rtx_freemem (rtx, ofs_free);
if (nflds > 1) hawk_rtx_refdownval (rtx, v);
return -1;
}
}
if (ofs_free) hawk_rtx_freemem (rtx, ofs_free);
if (nflds > 1) hawk_rtx_refdownval (rtx, v);
v = (hawk_val_t*)hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(&tmp));
if (!v)
{
hawk_ooecs_fini (&tmp);
return -1;
}
hawk_rtx_refdownval (rtx, rtx->inrec.d0);
rtx->inrec.d0 = v;
hawk_rtx_refupval (rtx, rtx->inrec.d0);
hawk_ooecs_swap (&tmp, &rtx->inrec.line);
hawk_ooecs_fini (&tmp);
for (i = nflds; i < rtx->inrec.nflds; i++)
{
hawk_rtx_refdownval (rtx, rtx->inrec.flds[i].val);
}
rtx->inrec.nflds = nflds;
return 0;
}
static hawk_ooch_t* idxnde_to_str (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_ooch_t* buf, hawk_oow_t* len)
{
hawk_ooch_t* str;
hawk_val_t* idx;
HAWK_ASSERT (nde != HAWK_NULL);
if (!nde->next)
{
hawk_rtx_valtostr_out_t out;
/* single node index */
idx = eval_expression (rtx, nde);
if (!idx) return HAWK_NULL;
hawk_rtx_refupval (rtx, idx);
str = HAWK_NULL;
if (buf)
{
/* try with a fixed-size buffer if given */
out.type = HAWK_RTX_VALTOSTR_CPLCPY;
out.u.cplcpy.ptr = buf;
out.u.cplcpy.len = *len;
if (hawk_rtx_valtostr (rtx, idx, &out) >= 0)
{
str = out.u.cplcpy.ptr;
*len = out.u.cplcpy.len;
HAWK_ASSERT (str == buf);
}
}
if (!str)
{
/* if no fixed-size buffer was given or the fixed-size
* conversion failed, switch to the dynamic mode */
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
if (hawk_rtx_valtostr(rtx, idx, &out) <= -1)
{
hawk_rtx_refdownval (rtx, idx);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
str = out.u.cpldup.ptr;
*len = out.u.cpldup.len;
}
hawk_rtx_refdownval (rtx, idx);
}
else
{
/* multidimensional index */
hawk_ooecs_t idxstr;
hawk_oocs_t tmp;
hawk_rtx_valtostr_out_t out;
out.type = HAWK_RTX_VALTOSTR_STRPCAT;
out.u.strpcat = &idxstr;
if (hawk_ooecs_init(&idxstr, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)
{
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
while (nde)
{
idx = eval_expression(rtx, nde);
if (!idx)
{
hawk_ooecs_fini (&idxstr);
return HAWK_NULL;
}
hawk_rtx_refupval (rtx, idx);
if (HAWK_OOECS_LEN(&idxstr) > 0 && hawk_ooecs_ncat(&idxstr, rtx->gbl.subsep.ptr, rtx->gbl.subsep.len) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, idx);
hawk_ooecs_fini (&idxstr);
ADJERR_LOC (rtx, &nde->loc);
return HAWK_NULL;
}
if (hawk_rtx_valtostr(rtx, idx, &out) <= -1)
{
hawk_rtx_refdownval (rtx, idx);
hawk_ooecs_fini (&idxstr);
return HAWK_NULL;
}
hawk_rtx_refdownval (rtx, idx);
nde = nde->next;
}
hawk_ooecs_yield (&idxstr, &tmp, 0);
str = tmp.ptr;
*len = tmp.len;
hawk_ooecs_fini (&idxstr);
}
return str;
}
/* ========================================================================= */
hawk_ooch_t* hawk_rtx_format (
hawk_rtx_t* rtx, hawk_ooecs_t* out, hawk_ooecs_t* fbu,
const hawk_ooch_t* fmt, hawk_oow_t fmt_len,
hawk_oow_t nargs_on_stack, hawk_nde_t* args, hawk_oow_t* len)
{
hawk_oow_t i;
hawk_oow_t stack_arg_idx = 1;
hawk_val_t* val;
#define OUT_CHAR(c) do { if (hawk_ooecs_ccat(out, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define OUT_STR(ptr,len) do { if (hawk_ooecs_ncat(out, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define FMT_CHAR(c) do { if (hawk_ooecs_ccat(fbu, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define FMT_STR(ptr,len) do { if (hawk_ooecs_ncat(fbu, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define GROW(buf) do { \
if ((buf)->ptr) \
{ \
hawk_rtx_freemem (rtx, (buf)->ptr); \
(buf)->ptr = HAWK_NULL; \
} \
(buf)->len += (buf)->inc; \
(buf)->ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_ooch_t)); \
if ((buf)->ptr == HAWK_NULL) \
{ \
(buf)->len = 0; \
return HAWK_NULL; \
} \
} while(0)
#define GROW_WITH_INC(buf,incv) do { \
if ((buf)->ptr) \
{ \
hawk_rtx_freemem (rtx, (buf)->ptr); \
(buf)->ptr = HAWK_NULL; \
} \
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
(buf)->ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_ooch_t)); \
if ((buf)->ptr == HAWK_NULL) \
{ \
(buf)->len = 0; \
return HAWK_NULL; \
} \
} while(0)
/* run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called */
HAWK_ASSERT (rtx->format.tmp.ptr != HAWK_NULL);
if (nargs_on_stack == (hawk_oow_t)-1)
{
val = (hawk_val_t*)args;
nargs_on_stack = 2;
}
else
{
val = HAWK_NULL;
}
if (out == HAWK_NULL) out = &rtx->format.out;
if (fbu == HAWK_NULL) fbu = &rtx->format.fmt;
hawk_ooecs_clear (out);
hawk_ooecs_clear (fbu);
for (i = 0; i < fmt_len; i++)
{
hawk_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 (HAWK_OOECS_LEN(fbu) == 0)
{
/* format specifier is empty */
if (fmt[i] == HAWK_T('%'))
{
/* add % to format specifier (fbu) */
FMT_CHAR (fmt[i]);
}
else
{
/* normal output */
OUT_CHAR (fmt[i]);
}
continue;
}
/* handle flags */
flags = 0;
while (i < fmt_len)
{
switch (fmt[i])
{
case HAWK_T(' '):
flags |= FLAG_SPACE;
break;
case HAWK_T('#'):
flags |= FLAG_HASH;
break;
case HAWK_T('0'):
flags |= FLAG_ZERO;
break;
case HAWK_T('+'):
flags |= FLAG_PLUS;
break;
case HAWK_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] == HAWK_T('*'))
{
/* variable width/precision modifier.
* take the width/precision from a parameter and
* transform it to a fixed length format */
hawk_val_t* v;
int n;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint(rtx, v, &wp[wp_idx]);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
do
{
n = hawk_fmt_intmax_to_oocstr(
rtx->format.tmp.ptr,
rtx->format.tmp.len,
wp[wp_idx],
10 | HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL,
-1,
HAWK_T('\0'),
HAWK_NULL
);
if (n <= -1)
{
/* -n is the number of characters required
* including terminating null */
GROW_WITH_INC (&rtx->format.tmp, -n);
continue;
}
break;
}
while (1);
FMT_STR(rtx->format.tmp.ptr, n);
if (args == HAWK_NULL || val != HAWK_NULL) stack_arg_idx++;
else args = args->next;
i++;
}
else
{
/* fixed width/precision modifier */
if (i < fmt_len && hawk_is_ooch_digit(fmt[i]))
{
wp[wp_idx] = 0;
do
{
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - HAWK_T('0');
FMT_CHAR (fmt[i]); i++;
}
while (i < fmt_len && hawk_is_ooch_digit(fmt[i]));
}
}
if (wp_idx == WP_WIDTH && i < fmt_len && fmt[i] == HAWK_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] == HAWK_T('d') || fmt[i] == HAWK_T('i') ||
fmt[i] == HAWK_T('x') || fmt[i] == HAWK_T('X') ||
fmt[i] == HAWK_T('b') || fmt[i] == HAWK_T('B') ||
fmt[i] == HAWK_T('o'))
{
hawk_val_t* v;
hawk_int_t l;
int n;
int fmt_flags;
int fmt_uint = 0;
int fmt_width;
hawk_ooch_t fmt_fill = HAWK_T('\0');
const hawk_ooch_t* fmt_prefix = HAWK_NULL;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint (rtx, v, &l);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
fmt_flags = HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL;
if (l == 0 && wp[WP_PRECISION] == 0)
{
/* A zero value with a precision of zero produces
* no character. */
fmt_flags |= HAWK_FMT_INTMAX_NOZERO;
}
if (wp[WP_WIDTH] != -1)
{
/* Width must be greater than 0 if specified */
HAWK_ASSERT (wp[WP_WIDTH] > 0);
/* 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 = HAWK_T(' ');
if (flags & FLAG_MINUS)
{
/* left justification. need to fill the right side */
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
}
}
else
{
if (wp[WP_PRECISION] == -1)
{
/* precision not specified.
* FLAG_ZERO can take effect */
fmt_fill = HAWK_T('0');
fmt_flags |= HAWK_FMT_INTMAX_FILLCENTER;
}
else
{
fmt_fill = HAWK_T(' ');
}
}
}
else
{
fmt_fill = HAWK_T(' ');
if (flags & FLAG_MINUS)
{
/* left justification. need to fill the right side */
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
}
}
}
switch (fmt[i])
{
case HAWK_T('B'):
case HAWK_T('b'):
fmt_flags |= 2;
fmt_uint = 1;
if (l && (flags & FLAG_HASH))
{
/* A nonzero value is prefixed with 0b */
fmt_prefix = HAWK_T("0b");
}
break;
case HAWK_T('X'):
fmt_flags |= HAWK_FMT_INTMAX_UPPERCASE;
case HAWK_T('x'):
fmt_flags |= 16;
fmt_uint = 1;
if (l && (flags & FLAG_HASH))
{
/* A nonzero value is prefixed with 0x */
fmt_prefix = HAWK_T("0x");
}
break;
case HAWK_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 |= HAWK_FMT_INTMAX_ZEROLEAD;
}
break;
default:
fmt_flags |= 10;
if (flags & FLAG_PLUS)
fmt_flags |= HAWK_FMT_INTMAX_PLUSSIGN;
if (flags & FLAG_SPACE)
fmt_flags |= HAWK_FMT_INTMAX_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 hawk_int_t
* to hawk_uint_t is needed before passing it to
* hawk_fmt_uintmax_to_oocstr().
*
* Consider a value of -1 for example.
* -1 is a value with all bits set.
* If hawk_int_t is 4 bytes and hawk_uintmax_t
* is 8 bytes, the value is shown below for
* each type respectively .
* -1 - 0xFFFFFFFF (hawk_int_t)
* -1 - 0xFFFFFFFFFFFFFFFF (hawk_uintmax_t)
* Implicit typecasting of -1 from hawk_int_t to
* to hawk_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
* though 0xFFFFFFF is expected in hexadecimal.
*/
n = hawk_fmt_uintmax_to_oocstr (
rtx->format.tmp.ptr,
fmt_width,
(hawk_uint_t)l,
fmt_flags,
wp[WP_PRECISION],
fmt_fill,
fmt_prefix
);
}
else
{
n = hawk_fmt_intmax_to_oocstr (
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] == HAWK_T('e') || fmt[i] == HAWK_T('E') ||
fmt[i] == HAWK_T('g') || fmt[i] == HAWK_T('G') ||
fmt[i] == HAWK_T('f'))
{
hawk_val_t* v;
hawk_flt_t r;
int n;
#if defined(HAWK_USE_AWK_FLTMAX)
FMT_CHAR (HAWK_T('j'));
#else
FMT_CHAR (HAWK_T('z'));
#endif
FMT_CHAR (fmt[i]);
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg(rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression(rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoflt(rtx, v, &r);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;
}
else if (fmt[i] == HAWK_T('c'))
{
hawk_ooch_t ch;
hawk_oow_t ch_len;
hawk_val_t* v;
hawk_val_type_t vtype;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE (rtx, v);
switch (vtype)
{
case HAWK_VAL_NIL:
ch = HAWK_T('\0');
ch_len = 0;
break;
case HAWK_VAL_INT:
ch = (hawk_ooch_t)HAWK_RTX_GETINTFROMVAL (rtx, v);
ch_len = 1;
break;
case HAWK_VAL_FLT:
ch = (hawk_ooch_t)((hawk_val_flt_t*)v)->val;
ch_len = 1;
break;
case HAWK_VAL_STR:
ch_len = ((hawk_val_str_t*)v)->val.len;
if (ch_len > 0)
{
ch = ((hawk_val_str_t*)v)->val.ptr[0];
ch_len = 1;
}
else ch = HAWK_T('\0');
break;
case HAWK_VAL_MBS:
ch_len = ((hawk_val_mbs_t*)v)->val.len;
if (ch_len > 0)
{
ch = ((hawk_val_mbs_t*)v)->val.ptr[0];
ch_len = 1;
}
else ch = HAWK_T('\0');
break;
default:
hawk_rtx_refdownval (rtx, v);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EVALTOCHR);
return HAWK_NULL;
}
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] == 0 || wp[WP_PRECISION] > (hawk_int_t)ch_len)
{
wp[WP_PRECISION] = (hawk_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 (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
if (wp[WP_PRECISION] > 0)
{
if (hawk_ooecs_ccat(out, ch) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
}
if (flags & FLAG_MINUS)
{
/* left align */
while (wp[WP_WIDTH] > wp[WP_PRECISION])
{
if (hawk_ooecs_ccat (out, HAWK_T(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
hawk_rtx_refdownval (rtx, v);
}
else if (fmt[i] == HAWK_T('s') || fmt[i] == HAWK_T('k') || fmt[i] == HAWK_T('K'))
{
hawk_ooch_t* str_ptr, * str_free = HAWK_NULL;
hawk_oow_t str_len;
hawk_int_t k;
hawk_val_t* v;
hawk_val_type_t vtype;
int bytetostr_flagged_radix = 16;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
switch (vtype)
{
case HAWK_VAL_NIL:
str_ptr = HAWK_T("");
str_len = 0;
break;
case HAWK_VAL_STR:
str_ptr = ((hawk_val_str_t*)v)->val.ptr;
str_len = ((hawk_val_str_t*)v)->val.len;
break;
case HAWK_VAL_MBS:
#if defined(HAWK_OOCH_IS_BCH)
str_ptr = ((hawk_val_mbs_t*)v)->val.ptr;
str_len = ((hawk_val_mbs_t*)v)->val.len;
break;
#else
if (fmt[i] != HAWK_T('s'))
{
str_ptr = (hawk_ooch_t*)((hawk_val_mbs_t*)v)->val.ptr;
str_len = ((hawk_val_mbs_t*)v)->val.len;
break;
}
#endif
default:
{
if (v == val)
{
hawk_rtx_refdownval (rtx, v);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTCNV);
return HAWK_NULL;
}
str_ptr = hawk_rtx_valtooocstrdup(rtx, v, &str_len);
if (!str_ptr)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
str_free = str_ptr;
break;
}
}
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] > (hawk_int_t)str_len) wp[WP_PRECISION] = (hawk_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 (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1)
{
if (str_free) hawk_rtx_freemem (rtx, str_free);
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
if (fmt[i] == HAWK_T('k')) bytetostr_flagged_radix |= HAWK_BYTE_TO_BCSTR_LOWERCASE;
for (k = 0; k < wp[WP_PRECISION]; k++)
{
hawk_ooch_t curc;
#if defined(HAWK_OOCH_IS_BCH)
curc = str_ptr[k];
#else
if (vtype == HAWK_VAL_MBS && fmt[i] != HAWK_T('s'))
curc = (hawk_uint8_t)((hawk_bch_t*)str_ptr)[k];
else curc = str_ptr[k];
#endif
if (fmt[i] != HAWK_T('s') && !HAWK_BYTE_PRINTABLE(curc))
{
hawk_ooch_t xbuf[3];
if (curc <= 0xFF)
{
if (hawk_ooecs_ncat(out, HAWK_T("\\x"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr(curc, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
else if (curc <= 0xFFFF)
{
hawk_uint16_t u16 = curc;
if (hawk_ooecs_ncat(out, HAWK_T("\\u"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr((u16 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr(u16 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
else
{
hawk_uint32_t u32 = curc;
if (hawk_ooecs_ncat(out, HAWK_T("\\U"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr((u32 >> 24) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr((u32 >> 16) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr((u32 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_oocstr(u32 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
}
else
{
if (hawk_ooecs_ccat(out, curc) == (hawk_oow_t)-1)
{
s_fail:
if (str_free) hawk_rtx_freemem (rtx, str_free);
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
}
}
if (str_free) hawk_rtx_freemem (rtx, str_free);
if (flags & FLAG_MINUS)
{
/* left align */
while (wp[WP_WIDTH] > wp[WP_PRECISION])
{
if (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
hawk_rtx_refdownval (rtx, v);
}
else
{
if (fmt[i] != HAWK_T('%')) OUT_STR (HAWK_OOECS_PTR(fbu), HAWK_OOECS_LEN(fbu));
OUT_CHAR (fmt[i]);
goto skip_taking_arg;
}
if (args == HAWK_NULL || val != HAWK_NULL) stack_arg_idx++;
else args = args->next;
skip_taking_arg:
hawk_ooecs_clear (fbu);
}
/* flush uncompleted formatting sequence */
OUT_STR (HAWK_OOECS_PTR(fbu), HAWK_OOECS_LEN(fbu));
*len = HAWK_OOECS_LEN(out);
return HAWK_OOECS_PTR(out);
}
/* ========================================================================= */
hawk_bch_t* hawk_rtx_formatmbs (
hawk_rtx_t* rtx, hawk_becs_t* out, hawk_becs_t* fbu,
const hawk_bch_t* fmt, hawk_oow_t fmt_len,
hawk_oow_t nargs_on_stack, hawk_nde_t* args, hawk_oow_t* len)
{
hawk_oow_t i;
hawk_oow_t stack_arg_idx = 1;
hawk_val_t* val;
#define OUT_MCHAR(c) do { if (hawk_becs_ccat(out, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define OUT_MBS(ptr,len) do { if (hawk_becs_ncat(out, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define FMT_MCHAR(c) do { if (hawk_becs_ccat(fbu, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define FMT_MBS(ptr,len) do { if (hawk_becs_ncat(fbu, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
#define GROW_MBSBUF(buf) do { \
if ((buf)->ptr) \
{ \
hawk_rtx_freemem (rtx, (buf)->ptr); \
(buf)->ptr = HAWK_NULL; \
} \
(buf)->len += (buf)->inc; \
(buf)->ptr = (hawk_bch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_bch_t)); \
if ((buf)->ptr == HAWK_NULL) \
{ \
(buf)->len = 0; \
return HAWK_NULL; \
} \
} while(0)
#define GROW_MBSBUF_WITH_INC(buf,incv) do { \
if ((buf)->ptr) \
{ \
hawk_rtx_freemem (rtx, (buf)->ptr); \
(buf)->ptr = HAWK_NULL; \
} \
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
(buf)->ptr = (hawk_bch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_bch_t)); \
if ((buf)->ptr == HAWK_NULL) \
{ \
(buf)->len = 0; \
return HAWK_NULL; \
} \
} while(0)
/* run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called */
HAWK_ASSERT (rtx->formatmbs.tmp.ptr != HAWK_NULL);
if (nargs_on_stack == (hawk_oow_t)-1)
{
val = (hawk_val_t*)args;
nargs_on_stack = 2;
}
else
{
val = HAWK_NULL;
}
if (out == HAWK_NULL) out = &rtx->formatmbs.out;
if (fbu == HAWK_NULL) fbu = &rtx->formatmbs.fmt;
hawk_becs_clear (out);
hawk_becs_clear (fbu);
for (i = 0; i < fmt_len; i++)
{
hawk_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 (HAWK_BECS_LEN(fbu) == 0)
{
/* format specifier is empty */
if (fmt[i] == HAWK_BT('%'))
{
/* 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 HAWK_BT(' '):
flags |= FLAG_SPACE;
break;
case HAWK_BT('#'):
flags |= FLAG_HASH;
break;
case HAWK_BT('0'):
flags |= FLAG_ZERO;
break;
case HAWK_BT('+'):
flags |= FLAG_PLUS;
break;
case HAWK_BT('-'):
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] == HAWK_BT('*'))
{
/* variable width/precision modifier.
* take the width/precision from a parameter and
* transform it to a fixed length format */
hawk_val_t* v;
int n;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg(rtx, stack_arg_idx);
}
else
{
if (val)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression(rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint(rtx, v, &wp[wp_idx]);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
do
{
n = hawk_fmt_intmax_to_bcstr (
rtx->formatmbs.tmp.ptr,
rtx->formatmbs.tmp.len,
wp[wp_idx],
10 | HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL,
-1,
HAWK_BT('\0'),
HAWK_NULL
);
if (n <= -1)
{
/* -n is the number of characters required
* including terminating null */
GROW_MBSBUF_WITH_INC (&rtx->formatmbs.tmp, -n);
continue;
}
break;
}
while (1);
FMT_MBS(rtx->formatmbs.tmp.ptr, n);
if (args == HAWK_NULL || val != HAWK_NULL) stack_arg_idx++;
else args = args->next;
i++;
}
else
{
/* fixed width/precision modifier */
if (i < fmt_len && hawk_is_bch_digit(fmt[i]))
{
wp[wp_idx] = 0;
do
{
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - HAWK_BT('0');
FMT_MCHAR (fmt[i]); i++;
}
while (i < fmt_len && hawk_is_bch_digit(fmt[i]));
}
}
if (wp_idx == WP_WIDTH && i < fmt_len && fmt[i] == HAWK_BT('.'))
{
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] == HAWK_BT('d') || fmt[i] == HAWK_BT('i') ||
fmt[i] == HAWK_BT('x') || fmt[i] == HAWK_BT('X') ||
fmt[i] == HAWK_BT('b') || fmt[i] == HAWK_BT('B') ||
fmt[i] == HAWK_BT('o'))
{
hawk_val_t* v;
hawk_int_t l;
int n;
int fmt_flags;
int fmt_uint = 0;
int fmt_width;
hawk_bch_t fmt_fill = HAWK_BT('\0');
const hawk_bch_t* fmt_prefix = HAWK_NULL;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoint (rtx, v, &l);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
fmt_flags = HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL;
if (l == 0 && wp[WP_PRECISION] == 0)
{
/* A zero value with a precision of zero produces
* no character. */
fmt_flags |= HAWK_FMT_INTMAX_NOZERO;
}
if (wp[WP_WIDTH] != -1)
{
/* Width must be greater than 0 if specified */
HAWK_ASSERT (wp[WP_WIDTH] > 0);
/* 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 = HAWK_BT(' ');
if (flags & FLAG_MINUS)
{
/* left justification. need to fill the right side */
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
}
}
else
{
if (wp[WP_PRECISION] == -1)
{
/* precision not specified.
* FLAG_ZERO can take effect */
fmt_fill = HAWK_BT('0');
fmt_flags |= HAWK_FMT_INTMAX_FILLCENTER;
}
else
{
fmt_fill = HAWK_BT(' ');
}
}
}
else
{
fmt_fill = HAWK_BT(' ');
if (flags & FLAG_MINUS)
{
/* left justification. need to fill the right side */
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
}
}
}
switch (fmt[i])
{
case HAWK_BT('B'):
case HAWK_BT('b'):
fmt_flags |= 2;
fmt_uint = 1;
if (l && (flags & FLAG_HASH))
{
/* A nonzero value is prefixed with 0b */
fmt_prefix = HAWK_BT("0b");
}
break;
case HAWK_BT('X'):
fmt_flags |= HAWK_FMT_INTMAX_UPPERCASE;
case HAWK_BT('x'):
fmt_flags |= 16;
fmt_uint = 1;
if (l && (flags & FLAG_HASH))
{
/* A nonzero value is prefixed with 0x */
fmt_prefix = HAWK_BT("0x");
}
break;
case HAWK_BT('o'):
fmt_flags |= 8;
fmt_uint = 1;
if (flags & 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 |= HAWK_FMT_INTMAX_ZEROLEAD;
}
break;
default:
fmt_flags |= 10;
if (flags & FLAG_PLUS)
fmt_flags |= HAWK_FMT_INTMAX_PLUSSIGN;
if (flags & FLAG_SPACE)
fmt_flags |= HAWK_FMT_INTMAX_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 hawk_int_t
* to hawk_uint_t is needed before passing it to
* hawk_fmt_uintmax_to_bcstr().
*
* Consider a value of -1 for example.
* -1 is a value with all bits set.
* If hawk_int_t is 4 bytes and hawk_uintmax_t
* is 8 bytes, the value is shown below for
* each type respectively .
* -1 - 0xFFFFFFFF (hawk_int_t)
* -1 - 0xFFFFFFFFFFFFFFFF (hawk_uintmax_t)
* Implicit typecasting of -1 from hawk_int_t to
* to hawk_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
* though 0xFFFFFFF is expected in hexadecimal.
*/
n = hawk_fmt_uintmax_to_bcstr (
rtx->formatmbs.tmp.ptr,
fmt_width,
(hawk_uint_t)l,
fmt_flags,
wp[WP_PRECISION],
fmt_fill,
fmt_prefix
);
}
else
{
n = hawk_fmt_intmax_to_bcstr (
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] == HAWK_BT('e') || fmt[i] == HAWK_BT('E') ||
fmt[i] == HAWK_BT('g') || fmt[i] == HAWK_BT('G') ||
fmt[i] == HAWK_BT('f'))
{
hawk_val_t* v;
hawk_flt_t r;
int n;
#if defined(HAWK_USE_AWK_FLTMAX)
FMT_MCHAR (HAWK_BT('j'));
#else
FMT_MCHAR (HAWK_BT('z'));
#endif
FMT_MCHAR (fmt[i]);
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg(rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression(rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_valtoflt(rtx, v, &r);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) return HAWK_NULL;
if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;
}
else if (fmt[i] == HAWK_BT('c'))
{
hawk_bch_t ch;
hawk_oow_t ch_len;
hawk_val_t* v;
hawk_val_type_t vtype;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg(rtx, stack_arg_idx);
}
else
{
if (val != HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression(rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE (rtx, v);
switch (vtype)
{
case HAWK_VAL_NIL:
ch = HAWK_BT('\0');
ch_len = 0;
break;
case HAWK_VAL_INT:
ch = (hawk_bch_t)HAWK_RTX_GETINTFROMVAL(rtx, v);
ch_len = 1;
break;
case HAWK_VAL_FLT:
ch = (hawk_bch_t)((hawk_val_flt_t*)v)->val;
ch_len = 1;
break;
case HAWK_VAL_STR:
ch_len = ((hawk_val_str_t*)v)->val.len;
if (ch_len > 0)
{
/* value truncation is expected */
ch = ((hawk_val_str_t*)v)->val.ptr[0];
ch_len = 1;
}
else ch = HAWK_BT('\0');
break;
case HAWK_VAL_MBS:
ch_len = ((hawk_val_mbs_t*)v)->val.len;
if (ch_len > 0)
{
ch = ((hawk_val_mbs_t*)v)->val.ptr[0];
ch_len = 1;
}
else ch = HAWK_BT('\0');
break;
default:
hawk_rtx_refdownval (rtx, v);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EVALTOCHR);
return HAWK_NULL;
}
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] == 0 || wp[WP_PRECISION] > (hawk_int_t)ch_len)
{
wp[WP_PRECISION] = (hawk_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 (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
if (wp[WP_PRECISION] > 0)
{
if (hawk_becs_ccat(out, ch) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
}
if (flags & FLAG_MINUS)
{
/* left align */
while (wp[WP_WIDTH] > wp[WP_PRECISION])
{
if (hawk_becs_ccat (out, HAWK_BT(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
hawk_rtx_refdownval (rtx, v);
}
else if (fmt[i] == HAWK_BT('s') || fmt[i] == HAWK_BT('k') || fmt[i] == HAWK_BT('K'))
{
hawk_bch_t* str_ptr, * str_free = HAWK_NULL;
hawk_oow_t str_len;
hawk_int_t k;
hawk_val_t* v;
hawk_val_type_t vtype;
int bytetombs_flagged_radix = 16;
if (args == HAWK_NULL)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = hawk_rtx_getarg (rtx, stack_arg_idx);
}
else
{
if (val)
{
if (stack_arg_idx >= nargs_on_stack)
{
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTARG);
return HAWK_NULL;
}
v = val;
}
else
{
v = eval_expression (rtx, args);
if (v == HAWK_NULL) return HAWK_NULL;
}
}
hawk_rtx_refupval (rtx, v);
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
switch (vtype)
{
case HAWK_VAL_NIL:
str_ptr = HAWK_BT("");
str_len = 0;
break;
case HAWK_VAL_MBS:
str_ptr = ((hawk_val_mbs_t*)v)->val.ptr;
str_len = ((hawk_val_mbs_t*)v)->val.len;
break;
case HAWK_VAL_STR:
#if defined(HAWK_OOCH_IS_BCH)
str_ptr = ((hawk_val_str_t*)v)->val.ptr;
str_len = ((hawk_val_str_t*)v)->val.len;
break;
#else
if (fmt[i] != HAWK_BT('s'))
{
/* arrange to print the wide character string byte by byte regardless of byte order */
str_ptr = (hawk_bch_t*)((hawk_val_str_t*)v)->val.ptr;
str_len = ((hawk_val_str_t*)v)->val.len * (HAWK_SIZEOF_OOCH_T / HAWK_SIZEOF_BCH_T);
break;
}
/* fall thru */
#endif
default:
{
if (v == val)
{
hawk_rtx_refdownval (rtx, v);
hawk_rtx_seterrnum (rtx, HAWK_NULL, HAWK_EFMTCNV);
return HAWK_NULL;
}
str_ptr = hawk_rtx_valtobcstrdup(rtx, v, &str_len);
if (!str_ptr)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
str_free = str_ptr;
break;
}
}
if (wp[WP_PRECISION] == -1 || wp[WP_PRECISION] > (hawk_int_t)str_len) wp[WP_PRECISION] = (hawk_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 (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1)
{
if (str_free) hawk_rtx_freemem (rtx, str_free);
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
if (fmt[i] == HAWK_BT('k')) bytetombs_flagged_radix |= HAWK_BYTE_TO_BCSTR_LOWERCASE;
for (k = 0; k < wp[WP_PRECISION]; k++)
{
hawk_bch_t curc;
curc = str_ptr[k];
if (fmt[i] != HAWK_BT('s') && !HAWK_BYTE_PRINTABLE(curc))
{
hawk_bch_t xbuf[3];
if (curc <= 0xFF)
{
if (hawk_becs_ncat(out, HAWK_BT("\\x"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr(curc, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
else if (curc <= 0xFFFF)
{
hawk_uint16_t u16 = curc;
if (hawk_becs_ncat(out, HAWK_BT("\\u"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr((u16 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr(u16 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
else
{
hawk_uint32_t u32 = curc;
if (hawk_becs_ncat(out, HAWK_BT("\\U"), 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr((u32 >> 24) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr((u32 >> 16) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr((u32 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
hawk_byte_to_bcstr(u32 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto s_fail;
}
}
else
{
if (hawk_becs_ccat(out, curc) == (hawk_oow_t)-1)
{
s_fail:
if (str_free) hawk_rtx_freemem (rtx, str_free);
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
}
}
if (str_free) hawk_rtx_freemem (rtx, str_free);
if (flags & FLAG_MINUS)
{
/* left align */
while (wp[WP_WIDTH] > wp[WP_PRECISION])
{
if (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1)
{
hawk_rtx_refdownval (rtx, v);
return HAWK_NULL;
}
wp[WP_WIDTH]--;
}
}
hawk_rtx_refdownval (rtx, v);
}
else
{
if (fmt[i] != HAWK_BT('%')) OUT_MBS (HAWK_BECS_PTR(fbu), HAWK_BECS_LEN(fbu));
OUT_MCHAR (fmt[i]);
goto skip_taking_arg;
}
if (args == HAWK_NULL || val != HAWK_NULL) stack_arg_idx++;
else args = args->next;
skip_taking_arg:
hawk_becs_clear (fbu);
}
/* flush uncompleted formatting sequence */
OUT_MBS (HAWK_BECS_PTR(fbu), HAWK_BECS_LEN(fbu));
*len = HAWK_BECS_LEN(out);
return HAWK_BECS_PTR(out);
}
/* ------------------------------------------------------------------------- */
void hawk_rtx_setnrflt (hawk_rtx_t* rtx, const hawk_nrflt_t* nrflt)
{
rtx->nrflt = *nrflt;
}
void hawk_rtx_getnrflt (hawk_rtx_t* rtx, hawk_nrflt_t* nrflt)
{
*nrflt = rtx->nrflt;
}
/* ------------------------------------------------------------------------ */
hawk_oow_t hawk_rtx_fmttoucstr_ (hawk_rtx_t* rtx, hawk_uch_t* buf, hawk_oow_t bufsz, const hawk_uch_t* fmt, ...)
{
va_list ap;
hawk_oow_t n;
va_start(ap, fmt);
n = hawk_gem_vfmttoucstr(hawk_rtx_getgem(rtx), buf, bufsz, fmt, ap);
va_end(ap);
return n;
}
hawk_oow_t hawk_rtx_fmttobcstr_ (hawk_rtx_t* rtx, hawk_bch_t* buf, hawk_oow_t bufsz, const hawk_bch_t* fmt, ...)
{
va_list ap;
hawk_oow_t n;
va_start(ap, fmt);
n = hawk_gem_vfmttobcstr(hawk_rtx_getgem(rtx), buf, bufsz, fmt, ap);
va_end(ap);
return n;
}
/* ------------------------------------------------------------------------ */
int hawk_rtx_buildrex (hawk_rtx_t* rtx, const hawk_ooch_t* ptn, hawk_oow_t len, hawk_tre_t** code, hawk_tre_t** icode)
{
return hawk_gem_buildrex(hawk_rtx_getgem(rtx), ptn, len, !(hawk_rtx_gethawk(rtx)->opt.trait & HAWK_REXBOUND), code, icode);
}