diff options
-rw-r--r-- | ChangeLog | 69 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | Makefile.in | 16 | ||||
-rw-r--r-- | TODO.NUMH | 40 | ||||
-rw-r--r-- | TODO.decimal | 7 | ||||
-rw-r--r-- | array.c | 56 | ||||
-rw-r--r-- | awk.h | 277 | ||||
-rw-r--r-- | awkgram.c | 1140 | ||||
-rw-r--r-- | awkgram.y | 422 | ||||
-rw-r--r-- | awklib/ChangeLog | 4 | ||||
-rw-r--r-- | builtin.c | 1784 | ||||
-rw-r--r-- | command.c | 34 | ||||
-rw-r--r-- | command.y | 34 | ||||
-rw-r--r-- | debug.c | 33 | ||||
-rw-r--r-- | double.c | 1641 | ||||
-rw-r--r-- | eval.c | 250 | ||||
-rw-r--r-- | field.c | 24 | ||||
-rw-r--r-- | format.c | 791 | ||||
-rw-r--r-- | format.h | 189 | ||||
-rw-r--r-- | gawkapi.c | 7 | ||||
-rw-r--r-- | int_array.c | 2 | ||||
-rw-r--r-- | interpret.h | 172 | ||||
-rw-r--r-- | io.c | 65 | ||||
-rw-r--r-- | main.c | 118 | ||||
-rw-r--r-- | misc/ap_math.awk | 331 | ||||
-rw-r--r-- | mpfr.c | 1802 | ||||
-rw-r--r-- | msg.c | 54 | ||||
-rw-r--r-- | node.c | 339 | ||||
-rw-r--r-- | profile.c | 52 |
29 files changed, 5294 insertions, 4462 deletions
@@ -1,3 +1,10 @@ +2014-09-16 Arnold D. Robbins <arnold@skeeve.com> + + * mpfr.c (cleanup_mpfr): Removed. + (do_mpfp_div): Merged in. Other changes to make things work. + * awkgram.y (tokentab): Need a new flag for "div" instead of + checking the builtin function. + 2014-09-15 Arnold D. Robbins <arnold@skeeve.com> Finish removing use of isalpha and isalnum. @@ -639,6 +646,11 @@ include stdlib.h. Needed for Illumos. Thanks to Richard Palo <richard.palo@free.fr> for the report. +2013-12-23 Arnold D. Robbins <arnold@skeeve.com> + + * eval.c, interpret.h, mpfr.c: Remove unused variables, fix + "variable may be used but not initialized" warnings. + 2013-12-21 Mike Frysinger <vapier@gentoo.org> * configure.ac: Add --disable-extensions flag to control @@ -1348,6 +1360,25 @@ * dfa.h: Include regex.h and stddef.h directly. * dfa.c: Adjust includes. +2013-01-12 John Haque <j.eh@mchsi.com> + + * format.c: New file. + * Makefile.am: Add format.c to the list of files. + * builtin.c (format_tree, format_nondecimal, fmt_parse, + get_fmt_buf, mbc_byte_count, mbc_char_count): Move to format.c. + +2013-01-11 John Haque <j.eh@mchsi.com> + + Finish format_tree() refactoring. + + * awk.h (struct fmt_list_item): New definition. + * builtin.c (fmt_parse): New routine to parse a single format code. + (format_tree): Adjusted. + * eval.c (fmt_index): (Pre-)compile and store format codes. + * double.c (format_awknum_val): Reworked to use compiled + format codes. + * mpfr.c (mpfp_format_val): Ditto. + 2013-01-11 John Haque <j.eh@mchsi.com> * awk.h (do_mpfr_rshift): Renamed from do_mpfr_rhift. @@ -1377,6 +1408,28 @@ * regex_internal.h (struct re_dfa_t): Restore ifdefs around __libc_lock_define, they really were needed. Bleah. +2013-01-03 John Haque <j.eh@mchsi.com> + + Refactor format_tree() to seperate number formatting code. + + * format.h: New file. + (format_spec, print_fmt_buf): Definitions. + (chksize, bchunk, bchunk_one, buf_adjust, buf2node, + tmpbuf_prepend, pr_fill, pr_num_tail, free_print_fmt_buf): + Inline routines. + * awk.h (num_handler_t): New fields gawk_format_printf, + gawk_isnan, gawk_isinf. Removed field gawk_format_nodes. + * builtin.c (chksize__internal, cpbuf_chksize__internal, + get_fmt_buf, format_nondecimal): New routines. + (format_tree): Restore function format_tree(). Adjusted to call + the current number formatting routine. + * double.c (awknum_isnan, awknum_isinf, format_awknum_printf): + New routines. + (format_nodes_awknum): Removed. + * mpfr.c (mpfp_isnan, mpfp_isinf, mpfp_format_printf): + New routines. + (mpfp_format_nodes): Removed. + 2013-01-01 Arnold D. Robbins <arnold@skeeve.com> Sync with GLIBC regex files. @@ -1387,11 +1440,27 @@ * regexec.c (check_node_accept_bytes): Restore decl with use from GLIBC code since this is LIBC case. +2012-12-28 John Haque <j.eh@mchsi.com> + + * double.c: Use make_awknum everywhere instead of make_number + in case a routine is called using not the current handler + or before initialization. + 2012-12-27 Arnold D. Robbins <arnold@skeeve.com> * builtin.c (do_print, do_printf): Use output_fp as default output for print/printf only if running under the debugger. Otherwise use stdout as Brian, Peter, and Al intended. + +2012-12-27 John Haque <j.eh@mchsi.com> + + Number handling interface. + + * awk.h (numbr_handler_t, bltin_t): New definitions. + * double.c: New file for C double numbers. + * mpfr.c: Reworked. + + Lots of other changes. 2012-12-25 Arnold D. Robbins <arnold@skeeve.com> diff --git a/Makefile.am b/Makefile.am index 3d1c8837..52ef5a47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,11 +88,14 @@ base_sources = \ debug.c \ dfa.c \ dfa.h \ + double.c \ eval.c \ ext.c \ field.c \ floatcomp.c \ floatmagic.h \ + format.c \ + format.h \ gawkapi.c \ gawkapi.h \ gawkmisc.c \ diff --git a/Makefile.in b/Makefile.in index 5c2a7f11..4b5fa69f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -133,11 +133,12 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)" PROGRAMS = $(bin_PROGRAMS) am__objects_1 = array.$(OBJEXT) awkgram.$(OBJEXT) builtin.$(OBJEXT) \ cint_array.$(OBJEXT) command.$(OBJEXT) debug.$(OBJEXT) \ - dfa.$(OBJEXT) eval.$(OBJEXT) ext.$(OBJEXT) field.$(OBJEXT) \ - floatcomp.$(OBJEXT) gawkapi.$(OBJEXT) gawkmisc.$(OBJEXT) \ - getopt.$(OBJEXT) getopt1.$(OBJEXT) int_array.$(OBJEXT) \ - io.$(OBJEXT) main.$(OBJEXT) mpfr.$(OBJEXT) msg.$(OBJEXT) \ - node.$(OBJEXT) profile.$(OBJEXT) random.$(OBJEXT) re.$(OBJEXT) \ + dfa.$(OBJEXT) double.$(OBJEXT) eval.$(OBJEXT) ext.$(OBJEXT) \ + field.$(OBJEXT) floatcomp.$(OBJEXT) format.$(OBJEXT) \ + gawkapi.$(OBJEXT) gawkmisc.$(OBJEXT) getopt.$(OBJEXT) \ + getopt1.$(OBJEXT) int_array.$(OBJEXT) io.$(OBJEXT) \ + main.$(OBJEXT) mpfr.$(OBJEXT) msg.$(OBJEXT) node.$(OBJEXT) \ + profile.$(OBJEXT) random.$(OBJEXT) re.$(OBJEXT) \ regex.$(OBJEXT) replace.$(OBJEXT) str_array.$(OBJEXT) \ symbol.$(OBJEXT) version.$(OBJEXT) am_gawk_OBJECTS = $(am__objects_1) @@ -492,11 +493,14 @@ base_sources = \ debug.c \ dfa.c \ dfa.h \ + double.c \ eval.c \ ext.c \ field.c \ floatcomp.c \ floatmagic.h \ + format.c \ + format.h \ gawkapi.c \ gawkapi.h \ gawkmisc.c \ @@ -661,10 +665,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dfa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/double.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/field.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/floatcomp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gawkapi.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gawkmisc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ diff --git a/TODO.NUMH b/TODO.NUMH new file mode 100644 index 00000000..d95ed559 --- /dev/null +++ b/TODO.NUMH @@ -0,0 +1,40 @@ +* (Further) refactoring (s)printf formatting code: + + [1] Remove direct calls to format_tree() in number to string (force_string) +routines. Fix CONVFMT = "%s"/"%c" crash; present in all known gawk versions. +Cache parsed format code in fmt_idx() ... DONE + + [2] Try to format NaN/inf in main gawk code, and not in the individual handlers. + +* In non-number related routines (substr, index, ..), fetch an integer directly +using get_number_si/get_number_ui to get the correct size at the int boundaries. +Use isinteger() to test for a floating-point value instead of get_number_d() and ... +May require additional tests e.g. SIZE_MAX <= UINT_MAX. In a nutshell, don't do this: + + number => double => integer + +* Restore constant-folding code for numbers. + +* Consider special handling of integer-indexed arrays with non-double +number handlers. + - Choose a reasonable range for integer indices. + #if SIZEOF_LONG <= 8 + typedef gawk_int_t long + #define GAWK_INT_MAX LONG_MAX + #define GAWK_INT_MIN LONG_MIN + #define GAWK_INT_BIT SIZEOF_LONG * 8 ### may not need this + #else + typedef gawk_int_t int + #define GAWK_INT_MAX INT_MAX + #define GAWK_INT_MIN INT_MIN + #define GAWK_INT_BIT SIZEOF_INT * 8 + #endif + Install gaurd code in array handlers (or adjust the defs) for size + anything but 4 or 8 (overthinking?). Use gawk_int_t instead + of int32_t or int64_t. + + - Use additional struct field number_fits_gawk_int() + (MPFR has similarly named routines, e.g. mpfr_fits_slong()), + and avoid checking after conversion. Note that the type is + gawk_int_t and NOT necessarily long. + diff --git a/TODO.decimal b/TODO.decimal new file mode 100644 index 00000000..e40cddbd --- /dev/null +++ b/TODO.decimal @@ -0,0 +1,7 @@ +Fri Mar 15 14:07:55 IST 2013 +============================ + +This branch needs to be merged with master. + +The eventual goal is to support decimal arithmetic with the mpdecimal library +at http://www.bytereef.org/mpdecimal/index.html @@ -26,7 +26,6 @@ #include "awk.h" extern FILE *output_fp; -extern NODE **fmt_list; /* declared in eval.c */ static size_t SUBSEPlen; static char *SUBSEP; @@ -72,14 +71,13 @@ register_array_func(afunc_t *afunc) return false; } - /* array_init --- register all builtin array types */ void array_init() { (void) register_array_func(str_array_func); /* the default */ - if (! do_mpfr) { + if (numbr_hndlr == & awknum_hndlr) { (void) register_array_func(int_array_func); (void) register_array_func(cint_array_func); } @@ -655,10 +653,6 @@ do_delete_loop(NODE *symbol, NODE **lhs) static void value_info(NODE *n) { - -#define PREC_NUM -1 -#define PREC_STR -1 - if (n == Nnull_string || n == Null_field) { fprintf(output_fp, "<(null)>"); return; @@ -666,30 +660,12 @@ value_info(NODE *n) if ((n->flags & (STRING|STRCUR)) != 0) { fprintf(output_fp, "<"); - fprintf(output_fp, "\"%.*s\"", PREC_STR, n->stptr); - if ((n->flags & (NUMBER|NUMCUR)) != 0) { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - fprintf(output_fp, ":%s", - mpg_fmt("%.*R*g", PREC_NUM, ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - fprintf(output_fp, ":%s", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - fprintf(output_fp, ":%.*g", PREC_NUM, n->numbr); - } + fprintf(output_fp, "\"%.*s\"", -1, n->stptr); + if ((n->flags & (NUMBER|NUMCUR)) != 0) + fprintf(output_fp, ":%s", fmt_number("%.17g", n)); fprintf(output_fp, ">"); - } else { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - fprintf(output_fp, "<%s>", - mpg_fmt("%.*R*g", PREC_NUM, ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - fprintf(output_fp, "<%s>", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - fprintf(output_fp, "<%.*g>", PREC_NUM, n->numbr); - } + } else + fprintf(output_fp, "<%s>", fmt_number("%.17g", n)); fprintf(output_fp, ":%s", flags2str(n->flags)); @@ -702,11 +678,8 @@ value_info(NODE *n) fprintf(output_fp, "]["); fprintf(output_fp, "stfmt=%d, ", n->stfmt); fprintf(output_fp, "CONVFMT=\"%s\"", n->stfmt <= -1 ? "%ld" - : fmt_list[n->stfmt]->stptr); + : fmt_list[n->stfmt].fmt->stptr); } - -#undef PREC_NUM -#undef PREC_STR } @@ -1220,22 +1193,11 @@ sort_user_func(const void *p1, const void *p2) PUSH(val2); /* execute the comparison function */ - (void) (*interpret)(code); + interpret(code); /* return value of the comparison function */ r = POP_NUMBER(); -#ifdef HAVE_MPFR - /* - * mpfr_sgn(mpz_sgn): Returns a positive value if op > 0, - * zero if op = 0, and a negative value if op < 0. - */ - if (is_mpg_float(r)) - ret = mpfr_sgn(r->mpg_numbr); - else if (is_mpg_integer(r)) - ret = mpz_sgn(r->mpg_i); - else -#endif - ret = (r->numbr < 0.0) ? -1 : (r->numbr > 0.0); + ret = sgn_number(r); DEREF(r); return ret; } @@ -203,18 +203,6 @@ extern void *memset_ulong(void *dest, int val, unsigned long l); /* same thing for warning */ #define warning (*(set_loc(__FILE__, __LINE__),r_warning)) -#ifdef HAVE_MPFR -#include <gmp.h> -#include <mpfr.h> -#ifndef MPFR_RNDN -/* for compatibility with MPFR 2.X */ -#define MPFR_RNDN GMP_RNDN -#define MPFR_RNDZ GMP_RNDZ -#define MPFR_RNDU GMP_RNDU -#define MPFR_RNDD GMP_RNDD -#endif -#endif - #include "regex.h" #include "dfa.h" typedef struct Regexp { @@ -237,6 +225,13 @@ typedef struct Regexp { #define RE_NEED_START 1 /* need to know start/end of match */ #define RE_NO_BOL 2 /* not allowed to match ^ in regexp */ + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#define DEFAULT_G_PRECISION 6 + #include "gawkapi.h" /* Stuff for losing systems. */ @@ -380,17 +375,6 @@ typedef struct exp_node { } nodep; struct { -#ifdef HAVE_MPFR - union { - AWKNUM fltnum; - mpfr_t mpnum; - mpz_t mpi; - } nm; -#else - AWKNUM fltnum; /* this is here for optimal packing of - * the structure on many machines - */ -#endif char *sp; size_t slen; long sref; @@ -398,7 +382,14 @@ typedef struct exp_node { #if MBS_SUPPORT wchar_t *wsp; size_t wslen; -#endif +#endif + union { + AWKNUM fltnum; + void *pq; + } nmb; +#define numbr sub.val.nmb.fltnum +#define qnumbr sub.val.nmb.pq + } val; } sub; NODETYPE type; @@ -464,13 +455,6 @@ typedef struct exp_node { #define stfmt sub.val.idx #define wstptr sub.val.wsp #define wstlen sub.val.wslen -#ifdef HAVE_MPFR -#define mpg_numbr sub.val.nm.mpnum -#define mpg_i sub.val.nm.mpi -#define numbr sub.val.nm.fltnum -#else -#define numbr sub.val.fltnum -#endif /* Node_arrayfor */ #define for_list sub.nodep.r.av @@ -548,18 +532,19 @@ typedef enum opcodeval { Op_illegal, /* binary operators */ + Op_plus, + Op_minus, Op_times, - Op_times_i, Op_quotient, - Op_quotient_i, Op_mod, - Op_mod_i, - Op_plus, - Op_plus_i, - Op_minus, - Op_minus_i, Op_exp, - Op_exp_i, + Op_assign_plus, + Op_assign_minus, + Op_assign_times, + Op_assign_quotient, + Op_assign_mod, + Op_assign_exp, + Op_concat, /* line range instruction pair */ @@ -574,6 +559,7 @@ typedef enum opcodeval { Op_predecrement, Op_postincrement, Op_postdecrement, + Op_unary_plus, Op_unary_minus, Op_field_spec, @@ -585,12 +571,6 @@ typedef enum opcodeval { Op_store_var, /* simple variable assignment optimization */ Op_store_sub, /* array[subscript] assignment optimization */ Op_store_field, /* $n assignment optimization */ - Op_assign_times, - Op_assign_quotient, - Op_assign_mod, - Op_assign_plus, - Op_assign_minus, - Op_assign_exp, Op_assign_concat, /* boolean binaries */ @@ -747,7 +727,6 @@ typedef struct exp_instruction { #define GENSUB 0x02 /* builtin is gensub */ #define LITERAL 0x04 /* target is a literal string */ - /* Op_K_exit */ #define target_end d.di #define target_atexit x.xi @@ -877,6 +856,74 @@ typedef struct exp_instruction { /* Op_store_var */ #define initval x.xn + +typedef struct { + const char *name; /* name of the built-in */ + NODE *(*ptr)(int); /* function that implements this built-in */ +} bltin_t; + +struct format_spec; +struct print_fmt_buf; + +typedef struct { + bool (*init)(bltin_t **); /* initialization */ + const char *(*version_str)(void); /* library version */ + void (*load_procinfo)(void); /* load relevant PROCINFO entries */ + + NODE *(*gawk_make_number)(AWKNUM); /* convert AWKNUM to numeric value */ + NODE *(*gawk_str2number)(char *, char **, int, bool); /* convert a C-style string + to number */ + NODE *(*gawk_copy_number)(const NODE *); /* deep-copy a numeric NODE */ + + void (*gawk_free_number)(NODE *); /* free internally allocated space */ + + NODE *(*gawk_force_number)(NODE *); /* force a NODE value to be numeric */ + void (*gawk_negate_number)(NODE *); /* in place negation of a number */ + int (*gawk_cmp_numbers)(const NODE *, const NODE *); /* compare two numbers */ + + int (*gawk_sgn_number)(const NODE *); /* test if a numeric node is zero, + positive or negative */ + bool (*gawk_isinteger)(const NODE *); /* test if a number is an integer */ + bool (*gawk_isnan)(const NODE *); /* test if NaN */ + bool (*gawk_isinf)(const NODE *); /* test if infinity */ + + NODE *(*gawk_fmt_number)(const char *, int, NODE *); /* stringify a numeric value + based on awk input/output format */ + + /* (s)printf formatting of numbers */ + int (*gawk_format_printf)(NODE *, struct format_spec *, struct print_fmt_buf *); + + /* conversion to C types */ + double (*gawk_todouble)(const NODE *); /* number to double */ + long (*gawk_tolong)(const NODE *); /* number to long */ + unsigned long (*gawk_toulong)(const NODE *); /* number to unsigned long */ + uintmax_t (*gawk_touintmax_t)(const NODE *); /* number to uintmax_t */ + + /* operators */ + NODE *(*add)(const NODE *, const NODE *); /* addition */ + NODE *(*sub)(const NODE *, const NODE *); /* subtraction */ + NODE *(*mul)(const NODE *, const NODE *); /* multiplication */ + NODE *(*div)(const NODE *, const NODE *); /* division */ + NODE *(*mod)(const NODE *, const NODE *); /* remainder */ + NODE *(*pow)(const NODE *, const NODE *); /* exponentiation */ + + NODE *(*add_long)(const NODE *, long); /* add a long to a number */ + + NODE *(*update_numvar)(NODE *); /* update a NODE value from + internal variable(s) */ + void (*set_numvar)(const NODE *); /* update internal variable(s) + from a NODE value */ + long (*increment_var)(const NODE *, long); /* update NR or FNR related internal + variables -- efficiency hack */ + void (*init_numvars)(void); /* set default values for PREC etc. */ +} numbr_handler_t; + + +struct fmt_list_item { + NODE *fmt; /* format string */ + struct format_spec *spec; /* parsed format code */ +}; + typedef struct iobuf { awk_input_buf_t public; /* exposed to extensions */ char *buf; /* start data buffer */ @@ -1041,14 +1088,30 @@ extern NODE *LINT_node, *ERRNO_node, *TEXTDOMAIN_node, *FPAT_node; extern NODE *PREC_node, *ROUNDMODE_node; extern NODE *Nnull_string; extern NODE *Null_field; +extern NODE *true_node, *false_node; extern NODE **fields_arr; extern int sourceline; extern char *source; extern int (*interpret)(INSTRUCTION *); /* interpreter routine */ -extern NODE *(*make_number)(double); /* double instead of AWKNUM on purpose */ + +extern numbr_handler_t awknum_hndlr; /* double */ +extern numbr_handler_t mpfp_hndlr; /* arbitrary-precision floating-point */ +extern numbr_handler_t *numbr_hndlr; /* active handler */ + +extern NODE *(*make_number)(AWKNUM); extern NODE *(*str2number)(NODE *); extern NODE *(*format_val)(const char *, int, NODE *); extern int (*cmp_numbers)(const NODE *, const NODE *); +extern NODE *(*str2node)(char *, char **, int, bool); +extern void (*free_number)(NODE *); +extern unsigned long (*get_number_ui)(const NODE *); +extern long (*get_number_si)(const NODE *); +extern double (*get_number_d)(const NODE *); +extern uintmax_t (*get_number_uj)(const NODE *); +extern int (*sgn_number)(const NODE *); +extern int (*format_number_printf)(NODE *, struct format_spec *, struct print_fmt_buf *); + +extern struct fmt_list_item *fmt_list; /* built-in array types */ extern afunc_t str_array_func[]; @@ -1077,7 +1140,6 @@ enum do_flag_values { DO_SANDBOX = 0x0800, /* sandbox mode - disable 'system' function & redirections */ DO_PROFILE = 0x1000, /* profile the program */ DO_DEBUG = 0x2000, /* debug the program */ - DO_MPFR = 0x4000 /* arbitrary-precision floating-point math */ }; #define do_traditional (do_flags & DO_TRADITIONAL) @@ -1091,7 +1153,6 @@ enum do_flag_values { #define do_tidy_mem (do_flags & DO_TIDY_MEM) #define do_sandbox (do_flags & DO_SANDBOX) #define do_debug (do_flags & DO_DEBUG) -#define do_mpfr (do_flags & DO_MPFR) extern bool do_optimize; extern int use_lc_numeric; @@ -1119,15 +1180,6 @@ extern int ngroups; extern struct lconv loc; #endif /* HAVE_LOCALE_H */ -#ifdef HAVE_MPFR -extern mpfr_prec_t PRECISION; -extern mpfr_rnd_t ROUND_MODE; -extern mpz_t MNR; -extern mpz_t MFNR; -extern mpz_t mpzval; -extern bool do_ieee_fmt; /* emulate IEEE 754 floating-point format */ -#endif - extern const char *myname; extern const char def_strftime_format[]; @@ -1190,43 +1242,8 @@ DEREF(NODE *r) #define TOP_NUMBER() force_number(TOP_SCALAR()) /* ------------------------- Pseudo-functions ------------------------- */ -#ifdef HAVE_MPFR -/* conversion to C types */ -#define get_number_ui(n) (((n)->flags & MPFN) ? mpfr_get_ui((n)->mpg_numbr, ROUND_MODE) \ - : ((n)->flags & MPZN) ? mpz_get_ui((n)->mpg_i) \ - : (unsigned long) (n)->numbr) -#define get_number_si(n) (((n)->flags & MPFN) ? mpfr_get_si((n)->mpg_numbr, ROUND_MODE) \ - : ((n)->flags & MPZN) ? mpz_get_si((n)->mpg_i) \ - : (long) (n)->numbr) -#define get_number_d(n) (((n)->flags & MPFN) ? mpfr_get_d((n)->mpg_numbr, ROUND_MODE) \ - : ((n)->flags & MPZN) ? mpz_get_d((n)->mpg_i) \ - : (double) (n)->numbr) -#define get_number_uj(n) (((n)->flags & MPFN) ? mpfr_get_uj((n)->mpg_numbr, ROUND_MODE) \ - : ((n)->flags & MPZN) ? (uintmax_t) mpz_get_d((n)->mpg_i) \ - : (uintmax_t) (n)->numbr) - -#define iszero(n) (((n)->flags & MPFN) ? mpfr_zero_p((n)->mpg_numbr) \ - : ((n)->flags & MPZN) ? (mpz_sgn((n)->mpg_i) == 0) \ - : ((n)->numbr == 0.0)) - -#define IEEE_FMT(r, t) (void) (do_ieee_fmt && format_ieee(r, t)) - -#define mpg_float() mpg_node(MPFN) -#define mpg_integer() mpg_node(MPZN) -#define is_mpg_float(n) (((n)->flags & MPFN) != 0) -#define is_mpg_integer(n) (((n)->flags & MPZN) != 0) -#define is_mpg_number(n) (((n)->flags & (MPZN|MPFN)) != 0) -#else -#define get_number_ui(n) (unsigned long) (n)->numbr -#define get_number_si(n) (long) (n)->numbr -#define get_number_d(n) (double) (n)->numbr -#define get_number_uj(n) (uintmax_t) (n)->numbr - -#define is_mpg_number(n) 0 -#define is_mpg_float(n) 0 -#define is_mpg_integer(n) 0 -#define iszero(n) ((n)->numbr == 0.0) -#endif +#define iszero(n) (sgn_number(n) == 0) +#define isinteger(n) numbr_hndlr->gawk_isinteger(n) #define var_uninitialized(n) ((n)->var_value == Nnull_string) @@ -1369,7 +1386,6 @@ extern void free_srcfile(SRCFILE *thisfile); extern void register_deferred_variable(const char *name, NODE *(*load_func)(void)); extern int files_are_same(char *path, SRCFILE *src); extern void valinfo(NODE *n, Func_print print_func, FILE *fp); -extern void negate_num(NODE *n); typedef NODE *(*builtin_func_t)(int); /* function that implements a built-in */ extern builtin_func_t lookup_builtin(const char *name); extern void install_builtins(void); @@ -1377,19 +1393,13 @@ extern bool is_alpha(int c); extern bool is_alnum(int c); extern bool is_identchar(int c); /* builtin.c */ -extern double double_to_int(double d); -extern NODE *do_exp(int nargs); extern NODE *do_fflush(int nargs); extern NODE *do_index(int nargs); -extern NODE *do_int(int nargs); extern NODE *do_isarray(int nargs); extern NODE *do_length(int nargs); -extern NODE *do_log(int nargs); extern NODE *do_mktime(int nargs); extern NODE *do_sprintf(int nargs); extern void do_printf(int nargs, int redirtype); -extern void print_simple(NODE *tree, FILE *fp); -extern NODE *do_sqrt(int nargs); extern NODE *do_substr(int nargs); extern NODE *do_strftime(int nargs); extern NODE *do_systime(int nargs); @@ -1398,22 +1408,8 @@ extern void do_print(int nargs, int redirtype); extern void do_print_rec(int args, int redirtype); extern NODE *do_tolower(int nargs); extern NODE *do_toupper(int nargs); -extern NODE *do_atan2(int nargs); -extern NODE *do_sin(int nargs); -extern NODE *do_cos(int nargs); -extern NODE *do_rand(int nargs); -extern NODE *do_srand(int nargs); extern NODE *do_match(int nargs); extern NODE *do_sub(int nargs, unsigned int flags); -extern NODE *format_tree(const char *, size_t, NODE **, long); -extern NODE *do_lshift(int nargs); -extern NODE *do_rshift(int nargs); -extern NODE *do_and(int nargs); -extern NODE *do_or(int nargs); -extern NODE *do_xor(int nargs); -extern NODE *do_compl(int nargs); -extern NODE *do_strtonum(int nargs); -extern AWKNUM nondec2awknum(char *str, size_t len); extern NODE *do_dcgettext(int nargs); extern NODE *do_dcngettext(int nargs); extern NODE *do_bindtextdomain(int nargs); @@ -1422,12 +1418,12 @@ extern NODE *do_div(int nargs); extern int strncasecmpmbs(const unsigned char *, const unsigned char *, size_t); #endif + /* eval.c */ extern void PUSH_CODE(INSTRUCTION *cp); extern INSTRUCTION *POP_CODE(void); extern void init_interpret(void); extern int cmp_nodes(NODE *t1, NODE *t2); -extern int cmp_awknums(const NODE *t1, const NODE *t2); extern void set_IGNORECASE(void); extern void set_OFS(void); extern void set_ORS(void); @@ -1447,13 +1443,13 @@ extern const char *flags2str(int); extern const char *genflags2str(int flagval, const struct flagtab *tab); extern const char *nodetype2str(NODETYPE type); extern void load_casetable(void); -extern AWKNUM calc_exp(AWKNUM x1, AWKNUM x2); extern const char *opcode2str(OPCODE type); extern const char *op2str(OPCODE type); extern NODE **r_get_lhs(NODE *n, bool reference); extern STACK_ITEM *grow_stack(void); extern void dump_fcall_stack(FILE *fp); extern int register_exec_hook(Func_pre_exec preh, Func_post_exec posth); + /* ext.c */ extern NODE *do_ext(int nargs); void load_ext(const char *lib_name); /* temporary */ @@ -1467,12 +1463,15 @@ extern NODE *get_actual_argument(int, bool, bool); #define get_scalar_argument(i, opt) get_actual_argument((i), (opt), false) #define get_array_argument(i, opt) get_actual_argument((i), (opt), true) #endif + /* field.c */ extern void init_fields(void); extern void set_record(const char *buf, int cnt); extern void reset_record(void); extern void rebuild_record(void); extern void set_NF(void); +extern void set_PREC(void); +extern void set_ROUNDMODE(void); extern NODE **get_field(long num, Func_ptr *assign); extern NODE *do_split(int nargs); extern NODE *do_patsplit(int nargs); @@ -1521,7 +1520,6 @@ extern void register_output_wrapper(awk_output_wrapper_t *wrapper); extern void register_two_way_processor(awk_two_way_processor_t *processor); extern void set_FNR(void); extern void set_NR(void); - extern struct redirect *redirect(NODE *redir_exp, int redirtype, int *errflg); extern NODE *do_close(int nargs); extern int flush_io(void); @@ -1534,6 +1532,7 @@ extern NODE *do_getline(int intovar, IOBUF *iop); extern struct redirect *getredirect(const char *str, int len); extern bool inrec(IOBUF *iop, int *errcode); extern int nextfile(IOBUF **curfile, bool skipping); + /* main.c */ extern int arg_assign(char *arg, bool initing); extern int is_std_var(const char *var); @@ -1542,42 +1541,11 @@ extern char *estrdup(const char *str, size_t len); extern void update_global_values(); extern long getenv_long(const char *name); -/* mpfr.c */ -extern void set_PREC(void); -extern void set_ROUNDMODE(void); -extern void mpfr_unset(NODE *n); -#ifdef HAVE_MPFR -extern int mpg_cmp(const NODE *, const NODE *); -extern int format_ieee(mpfr_ptr, int); -extern NODE *mpg_update_var(NODE *); -extern long mpg_set_var(NODE *); -extern NODE *do_mpfr_and(int); -extern NODE *do_mpfr_atan2(int); -extern NODE *do_mpfr_compl(int); -extern NODE *do_mpfr_cos(int); -extern NODE *do_mpfr_div(int); -extern NODE *do_mpfr_exp(int); -extern NODE *do_mpfr_int(int); -extern NODE *do_mpfr_log(int); -extern NODE *do_mpfr_lshift(int); -extern NODE *do_mpfr_or(int); -extern NODE *do_mpfr_rand(int); -extern NODE *do_mpfr_rshift(int); -extern NODE *do_mpfr_sin(int); -extern NODE *do_mpfr_sqrt(int); -extern NODE *do_mpfr_srand(int); -extern NODE *do_mpfr_strtonum(int); -extern NODE *do_mpfr_xor(int); -extern void init_mpfr(mpfr_prec_t, const char *); -extern void cleanup_mpfr(void); -extern NODE *mpg_node(unsigned int); -extern const char *mpg_fmt(const char *, ...); -extern int mpg_strtoui(mpz_ptr, char *, size_t, char **, int); -#endif /* msg.c */ extern void gawk_exit(int status); extern void final_exit(int status) ATTRIBUTE_NORETURN; extern void err(bool isfatal, const char *s, const char *emsg, va_list argp) ATTRIBUTE_PRINTF(3, 0); +const char *fmt_number(const char *format, const NODE *n); extern void msg (const char *mesg, ...) ATTRIBUTE_PRINTF_1; extern void error (const char *mesg, ...) ATTRIBUTE_PRINTF_1; extern void r_warning (const char *mesg, ...) ATTRIBUTE_PRINTF_1; @@ -1588,6 +1556,7 @@ extern void (*lintfunc)(const char *mesg, ...) ATTRIBUTE_PRINTF_1; #else extern void (*lintfunc)(const char *mesg, ...); #endif + /* profile.c */ extern void init_profiling_signals(void); extern void set_prof_file(const char *filename); @@ -1598,6 +1567,7 @@ extern char *pp_node(NODE *n); extern int pp_func(INSTRUCTION *pc, void *); extern void pp_string_fp(Func_print print_func, FILE *fp, const char *str, size_t namelen, int delim, bool breaklines); + /* node.c */ extern NODE *r_force_number(NODE *n); extern NODE *r_format_val(const char *format, int index, NODE *s); @@ -1605,6 +1575,7 @@ extern NODE *r_dupnode(NODE *n); extern NODE *make_str_node(const char *s, size_t len, int flags); extern void *more_blocks(int id); extern int parse_escape(const char **string_ptr); +extern int get_numbase(const char *str, bool use_locale); #if MBS_SUPPORT extern NODE *str2wstr(NODE *n, size_t **ptr); extern NODE *wstr2str(NODE *n); @@ -1622,6 +1593,7 @@ extern void init_btowc_cache(); #else #define free_wstr(NODE) /* empty */ #endif + /* re.c */ extern Regexp *make_regexp(const char *s, size_t len, bool ignorecase, bool dfa, bool canfatal); extern int research(Regexp *rp, char *str, int start, size_t len, int flags); @@ -1632,7 +1604,6 @@ extern void resyntax(int syntax); extern void resetup(void); extern int avoid_dfa(NODE *re, char *str, size_t len); extern int reisstring(const char *text, size_t len, Regexp *re, const char *buf); -extern int get_numbase(const char *str, bool use_locale); /* symbol.c */ extern void load_symbols(); @@ -1771,7 +1742,7 @@ in_array(NODE *a, NODE *s) NODE **ret; ret = a->aexists(a, s); - + return ret ? *ret : NULL; } @@ -110,7 +110,6 @@ static INSTRUCTION *mk_expression_list(INSTRUCTION *list, INSTRUCTION *s1); static INSTRUCTION *mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, INSTRUCTION *incr, INSTRUCTION *body); static void fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, INSTRUCTION *c_target); -static INSTRUCTION *mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op); static INSTRUCTION *mk_boolean(INSTRUCTION *left, INSTRUCTION *right, INSTRUCTION *op); static INSTRUCTION *mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, INSTRUCTION *op); static INSTRUCTION *mk_getline(INSTRUCTION *op, INSTRUCTION *opt_var, INSTRUCTION *redir, int redirtype); @@ -195,7 +194,7 @@ extern double fmod(double x, double y); #define YYSTYPE INSTRUCTION * -#line 199 "awkgram.c" /* yacc.c:339 */ +#line 198 "awkgram.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus @@ -349,7 +348,7 @@ int yyparse (void); /* Copy the second part of user declarations. */ -#line 353 "awkgram.c" /* yacc.c:358 */ +#line 352 "awkgram.c" /* yacc.c:358 */ #ifdef short # undef short @@ -651,25 +650,25 @@ static const yytype_uint8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 198, 198, 200, 205, 206, 212, 224, 228, 239, - 245, 250, 258, 266, 268, 273, 281, 283, 289, 290, - 292, 318, 329, 340, 346, 355, 365, 367, 369, 375, - 380, 381, 385, 404, 403, 437, 439, 444, 445, 458, - 463, 464, 468, 470, 472, 479, 569, 611, 653, 766, - 773, 780, 790, 799, 808, 817, 828, 844, 843, 867, - 879, 879, 977, 977, 1010, 1040, 1046, 1047, 1053, 1054, - 1061, 1066, 1078, 1092, 1094, 1102, 1107, 1109, 1117, 1119, - 1128, 1129, 1137, 1142, 1142, 1153, 1157, 1165, 1166, 1169, - 1171, 1176, 1177, 1186, 1187, 1192, 1197, 1203, 1205, 1207, - 1214, 1215, 1221, 1222, 1227, 1229, 1234, 1236, 1244, 1249, - 1258, 1265, 1267, 1269, 1285, 1295, 1302, 1304, 1309, 1311, - 1313, 1321, 1323, 1328, 1330, 1335, 1337, 1339, 1389, 1391, - 1393, 1395, 1397, 1399, 1401, 1403, 1417, 1422, 1427, 1452, - 1458, 1460, 1462, 1464, 1466, 1468, 1473, 1477, 1509, 1511, - 1517, 1523, 1536, 1537, 1538, 1543, 1548, 1552, 1556, 1571, - 1584, 1589, 1625, 1643, 1644, 1650, 1651, 1656, 1658, 1665, - 1682, 1699, 1701, 1708, 1713, 1721, 1731, 1743, 1752, 1756, - 1760, 1764, 1768, 1772, 1775, 1777, 1781, 1785, 1789 + 0, 197, 197, 199, 204, 205, 211, 223, 227, 238, + 244, 249, 257, 265, 267, 272, 280, 282, 288, 289, + 291, 317, 328, 339, 345, 354, 364, 366, 368, 374, + 379, 380, 384, 403, 402, 436, 438, 443, 444, 457, + 462, 463, 467, 469, 471, 478, 568, 610, 652, 765, + 772, 779, 789, 798, 807, 816, 827, 843, 842, 866, + 878, 878, 976, 976, 1009, 1039, 1045, 1046, 1052, 1053, + 1060, 1065, 1077, 1091, 1093, 1101, 1106, 1108, 1116, 1118, + 1127, 1128, 1136, 1141, 1141, 1152, 1156, 1164, 1165, 1168, + 1170, 1175, 1176, 1185, 1186, 1191, 1196, 1202, 1204, 1206, + 1213, 1214, 1220, 1221, 1226, 1228, 1233, 1235, 1243, 1248, + 1257, 1264, 1266, 1268, 1284, 1294, 1301, 1303, 1308, 1310, + 1312, 1320, 1322, 1327, 1329, 1334, 1336, 1338, 1388, 1390, + 1392, 1394, 1396, 1398, 1400, 1402, 1416, 1421, 1426, 1451, + 1457, 1459, 1461, 1463, 1465, 1467, 1472, 1476, 1508, 1510, + 1516, 1522, 1535, 1536, 1537, 1542, 1547, 1551, 1555, 1570, + 1582, 1587, 1623, 1641, 1642, 1648, 1649, 1654, 1656, 1663, + 1680, 1697, 1699, 1706, 1711, 1719, 1729, 1741, 1750, 1754, + 1758, 1762, 1766, 1770, 1773, 1775, 1779, 1783, 1787 }; #endif @@ -1842,26 +1841,26 @@ yyreduce: switch (yyn) { case 3: -#line 201 "awkgram.y" /* yacc.c:1646 */ +#line 200 "awkgram.y" /* yacc.c:1646 */ { rule = 0; yyerrok; } -#line 1851 "awkgram.c" /* yacc.c:1646 */ +#line 1850 "awkgram.c" /* yacc.c:1646 */ break; case 5: -#line 207 "awkgram.y" /* yacc.c:1646 */ +#line 206 "awkgram.y" /* yacc.c:1646 */ { next_sourcefile(); if (sourcefile == srcfiles) process_deferred(); } -#line 1861 "awkgram.c" /* yacc.c:1646 */ +#line 1860 "awkgram.c" /* yacc.c:1646 */ break; case 6: -#line 213 "awkgram.y" /* yacc.c:1646 */ +#line 212 "awkgram.y" /* yacc.c:1646 */ { rule = 0; /* @@ -1870,19 +1869,19 @@ yyreduce: */ /* yyerrok; */ } -#line 1874 "awkgram.c" /* yacc.c:1646 */ +#line 1873 "awkgram.c" /* yacc.c:1646 */ break; case 7: -#line 225 "awkgram.y" /* yacc.c:1646 */ +#line 224 "awkgram.y" /* yacc.c:1646 */ { (void) append_rule((yyvsp[-1]), (yyvsp[0])); } -#line 1882 "awkgram.c" /* yacc.c:1646 */ +#line 1881 "awkgram.c" /* yacc.c:1646 */ break; case 8: -#line 229 "awkgram.y" /* yacc.c:1646 */ +#line 228 "awkgram.y" /* yacc.c:1646 */ { if (rule != Rule) { msg(_("%s blocks must have an action part"), ruletab[rule]); @@ -1893,39 +1892,39 @@ yyreduce: } else /* pattern rule with non-empty pattern */ (void) append_rule((yyvsp[-1]), NULL); } -#line 1897 "awkgram.c" /* yacc.c:1646 */ +#line 1896 "awkgram.c" /* yacc.c:1646 */ break; case 9: -#line 240 "awkgram.y" /* yacc.c:1646 */ +#line 239 "awkgram.y" /* yacc.c:1646 */ { in_function = NULL; (void) mk_function((yyvsp[-1]), (yyvsp[0])); yyerrok; } -#line 1907 "awkgram.c" /* yacc.c:1646 */ +#line 1906 "awkgram.c" /* yacc.c:1646 */ break; case 10: -#line 246 "awkgram.y" /* yacc.c:1646 */ +#line 245 "awkgram.y" /* yacc.c:1646 */ { want_source = false; yyerrok; } -#line 1916 "awkgram.c" /* yacc.c:1646 */ +#line 1915 "awkgram.c" /* yacc.c:1646 */ break; case 11: -#line 251 "awkgram.y" /* yacc.c:1646 */ +#line 250 "awkgram.y" /* yacc.c:1646 */ { want_source = false; yyerrok; } -#line 1925 "awkgram.c" /* yacc.c:1646 */ +#line 1924 "awkgram.c" /* yacc.c:1646 */ break; case 12: -#line 259 "awkgram.y" /* yacc.c:1646 */ +#line 258 "awkgram.y" /* yacc.c:1646 */ { if (include_source((yyvsp[0])) < 0) YYABORT; @@ -1933,23 +1932,23 @@ yyreduce: bcfree((yyvsp[0])); (yyval) = NULL; } -#line 1937 "awkgram.c" /* yacc.c:1646 */ +#line 1936 "awkgram.c" /* yacc.c:1646 */ break; case 13: -#line 267 "awkgram.y" /* yacc.c:1646 */ +#line 266 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 1943 "awkgram.c" /* yacc.c:1646 */ +#line 1942 "awkgram.c" /* yacc.c:1646 */ break; case 14: -#line 269 "awkgram.y" /* yacc.c:1646 */ +#line 268 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 1949 "awkgram.c" /* yacc.c:1646 */ +#line 1948 "awkgram.c" /* yacc.c:1646 */ break; case 15: -#line 274 "awkgram.y" /* yacc.c:1646 */ +#line 273 "awkgram.y" /* yacc.c:1646 */ { if (load_library((yyvsp[0])) < 0) YYABORT; @@ -1957,35 +1956,35 @@ yyreduce: bcfree((yyvsp[0])); (yyval) = NULL; } -#line 1961 "awkgram.c" /* yacc.c:1646 */ +#line 1960 "awkgram.c" /* yacc.c:1646 */ break; case 16: -#line 282 "awkgram.y" /* yacc.c:1646 */ +#line 281 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 1967 "awkgram.c" /* yacc.c:1646 */ +#line 1966 "awkgram.c" /* yacc.c:1646 */ break; case 17: -#line 284 "awkgram.y" /* yacc.c:1646 */ +#line 283 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 1973 "awkgram.c" /* yacc.c:1646 */ +#line 1972 "awkgram.c" /* yacc.c:1646 */ break; case 18: -#line 289 "awkgram.y" /* yacc.c:1646 */ +#line 288 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; rule = Rule; } -#line 1979 "awkgram.c" /* yacc.c:1646 */ +#line 1978 "awkgram.c" /* yacc.c:1646 */ break; case 19: -#line 291 "awkgram.y" /* yacc.c:1646 */ +#line 290 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); rule = Rule; } -#line 1985 "awkgram.c" /* yacc.c:1646 */ +#line 1984 "awkgram.c" /* yacc.c:1646 */ break; case 20: -#line 293 "awkgram.y" /* yacc.c:1646 */ +#line 292 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *tp; @@ -2011,11 +2010,11 @@ yyreduce: (yyval) = list_append(list_merge((yyvsp[-3]), (yyvsp[0])), tp); rule = Rule; } -#line 2015 "awkgram.c" /* yacc.c:1646 */ +#line 2014 "awkgram.c" /* yacc.c:1646 */ break; case 21: -#line 319 "awkgram.y" /* yacc.c:1646 */ +#line 318 "awkgram.y" /* yacc.c:1646 */ { static int begin_seen = 0; if (do_lint_old && ++begin_seen == 2) @@ -2026,11 +2025,11 @@ yyreduce: (yyvsp[0])->source_file = source; (yyval) = (yyvsp[0]); } -#line 2030 "awkgram.c" /* yacc.c:1646 */ +#line 2029 "awkgram.c" /* yacc.c:1646 */ break; case 22: -#line 330 "awkgram.y" /* yacc.c:1646 */ +#line 329 "awkgram.y" /* yacc.c:1646 */ { static int end_seen = 0; if (do_lint_old && ++end_seen == 2) @@ -2041,70 +2040,70 @@ yyreduce: (yyvsp[0])->source_file = source; (yyval) = (yyvsp[0]); } -#line 2045 "awkgram.c" /* yacc.c:1646 */ +#line 2044 "awkgram.c" /* yacc.c:1646 */ break; case 23: -#line 341 "awkgram.y" /* yacc.c:1646 */ +#line 340 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->in_rule = rule = BEGINFILE; (yyvsp[0])->source_file = source; (yyval) = (yyvsp[0]); } -#line 2055 "awkgram.c" /* yacc.c:1646 */ +#line 2054 "awkgram.c" /* yacc.c:1646 */ break; case 24: -#line 347 "awkgram.y" /* yacc.c:1646 */ +#line 346 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->in_rule = rule = ENDFILE; (yyvsp[0])->source_file = source; (yyval) = (yyvsp[0]); } -#line 2065 "awkgram.c" /* yacc.c:1646 */ +#line 2064 "awkgram.c" /* yacc.c:1646 */ break; case 25: -#line 356 "awkgram.y" /* yacc.c:1646 */ +#line 355 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[-3]) == NULL) (yyval) = list_create(instruction(Op_no_op)); else (yyval) = (yyvsp[-3]); } -#line 2076 "awkgram.c" /* yacc.c:1646 */ +#line 2075 "awkgram.c" /* yacc.c:1646 */ break; case 26: -#line 366 "awkgram.y" /* yacc.c:1646 */ +#line 365 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2082 "awkgram.c" /* yacc.c:1646 */ +#line 2081 "awkgram.c" /* yacc.c:1646 */ break; case 27: -#line 368 "awkgram.y" /* yacc.c:1646 */ +#line 367 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2088 "awkgram.c" /* yacc.c:1646 */ +#line 2087 "awkgram.c" /* yacc.c:1646 */ break; case 28: -#line 370 "awkgram.y" /* yacc.c:1646 */ +#line 369 "awkgram.y" /* yacc.c:1646 */ { yyerror(_("`%s' is a built-in function, it cannot be redefined"), tokstart); YYABORT; } -#line 2098 "awkgram.c" /* yacc.c:1646 */ +#line 2097 "awkgram.c" /* yacc.c:1646 */ break; case 29: -#line 376 "awkgram.y" /* yacc.c:1646 */ +#line 375 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2104 "awkgram.c" /* yacc.c:1646 */ +#line 2103 "awkgram.c" /* yacc.c:1646 */ break; case 32: -#line 386 "awkgram.y" /* yacc.c:1646 */ +#line 385 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[-5])->source_file = source; if (install_function((yyvsp[-4])->lextok, (yyvsp[-5]), (yyvsp[-2])) < 0) @@ -2115,17 +2114,17 @@ yyreduce: /* $4 already free'd in install_function */ (yyval) = (yyvsp[-5]); } -#line 2119 "awkgram.c" /* yacc.c:1646 */ +#line 2118 "awkgram.c" /* yacc.c:1646 */ break; case 33: -#line 404 "awkgram.y" /* yacc.c:1646 */ +#line 403 "awkgram.y" /* yacc.c:1646 */ { want_regexp = true; } -#line 2125 "awkgram.c" /* yacc.c:1646 */ +#line 2124 "awkgram.c" /* yacc.c:1646 */ break; case 34: -#line 406 "awkgram.y" /* yacc.c:1646 */ +#line 405 "awkgram.y" /* yacc.c:1646 */ { NODE *n, *exp; char *re; @@ -2154,23 +2153,23 @@ yyreduce: (yyval)->opcode = Op_match_rec; (yyval)->memory = n; } -#line 2158 "awkgram.c" /* yacc.c:1646 */ +#line 2157 "awkgram.c" /* yacc.c:1646 */ break; case 35: -#line 438 "awkgram.y" /* yacc.c:1646 */ +#line 437 "awkgram.y" /* yacc.c:1646 */ { bcfree((yyvsp[0])); } -#line 2164 "awkgram.c" /* yacc.c:1646 */ +#line 2163 "awkgram.c" /* yacc.c:1646 */ break; case 37: -#line 444 "awkgram.y" /* yacc.c:1646 */ +#line 443 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2170 "awkgram.c" /* yacc.c:1646 */ +#line 2169 "awkgram.c" /* yacc.c:1646 */ break; case 38: -#line 446 "awkgram.y" /* yacc.c:1646 */ +#line 445 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[0]) == NULL) (yyval) = (yyvsp[-1]); @@ -2183,40 +2182,40 @@ yyreduce: } yyerrok; } -#line 2187 "awkgram.c" /* yacc.c:1646 */ +#line 2186 "awkgram.c" /* yacc.c:1646 */ break; case 39: -#line 459 "awkgram.y" /* yacc.c:1646 */ +#line 458 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2193 "awkgram.c" /* yacc.c:1646 */ +#line 2192 "awkgram.c" /* yacc.c:1646 */ break; case 42: -#line 469 "awkgram.y" /* yacc.c:1646 */ +#line 468 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2199 "awkgram.c" /* yacc.c:1646 */ +#line 2198 "awkgram.c" /* yacc.c:1646 */ break; case 43: -#line 471 "awkgram.y" /* yacc.c:1646 */ +#line 470 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 2205 "awkgram.c" /* yacc.c:1646 */ +#line 2204 "awkgram.c" /* yacc.c:1646 */ break; case 44: -#line 473 "awkgram.y" /* yacc.c:1646 */ +#line 472 "awkgram.y" /* yacc.c:1646 */ { if (do_pretty_print) (yyval) = list_prepend((yyvsp[0]), instruction(Op_exec_count)); else (yyval) = (yyvsp[0]); } -#line 2216 "awkgram.c" /* yacc.c:1646 */ +#line 2215 "awkgram.c" /* yacc.c:1646 */ break; case 45: -#line 480 "awkgram.y" /* yacc.c:1646 */ +#line 479 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *dflt, *curr = NULL, *cexp, *cstmt; INSTRUCTION *ip, *nextc, *tbreak; @@ -2306,11 +2305,11 @@ yyreduce: break_allowed--; fix_break_continue(ip, tbreak, NULL); } -#line 2310 "awkgram.c" /* yacc.c:1646 */ +#line 2309 "awkgram.c" /* yacc.c:1646 */ break; case 46: -#line 570 "awkgram.y" /* yacc.c:1646 */ +#line 569 "awkgram.y" /* yacc.c:1646 */ { /* * ----------------- @@ -2352,11 +2351,11 @@ yyreduce: continue_allowed--; fix_break_continue(ip, tbreak, tcont); } -#line 2356 "awkgram.c" /* yacc.c:1646 */ +#line 2355 "awkgram.c" /* yacc.c:1646 */ break; case 47: -#line 612 "awkgram.y" /* yacc.c:1646 */ +#line 611 "awkgram.y" /* yacc.c:1646 */ { /* * ----------------- @@ -2398,11 +2397,11 @@ yyreduce: } /* else $1 and $4 are NULLs */ } -#line 2402 "awkgram.c" /* yacc.c:1646 */ +#line 2401 "awkgram.c" /* yacc.c:1646 */ break; case 48: -#line 654 "awkgram.y" /* yacc.c:1646 */ +#line 653 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *ip; char *var_name = (yyvsp[-5])->lextok; @@ -2515,44 +2514,44 @@ regular_loop: break_allowed--; continue_allowed--; } -#line 2519 "awkgram.c" /* yacc.c:1646 */ +#line 2518 "awkgram.c" /* yacc.c:1646 */ break; case 49: -#line 767 "awkgram.y" /* yacc.c:1646 */ +#line 766 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_for_loop((yyvsp[-11]), (yyvsp[-9]), (yyvsp[-6]), (yyvsp[-3]), (yyvsp[0])); break_allowed--; continue_allowed--; } -#line 2530 "awkgram.c" /* yacc.c:1646 */ +#line 2529 "awkgram.c" /* yacc.c:1646 */ break; case 50: -#line 774 "awkgram.y" /* yacc.c:1646 */ +#line 773 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_for_loop((yyvsp[-10]), (yyvsp[-8]), (INSTRUCTION *) NULL, (yyvsp[-3]), (yyvsp[0])); break_allowed--; continue_allowed--; } -#line 2541 "awkgram.c" /* yacc.c:1646 */ +#line 2540 "awkgram.c" /* yacc.c:1646 */ break; case 51: -#line 781 "awkgram.y" /* yacc.c:1646 */ +#line 780 "awkgram.y" /* yacc.c:1646 */ { if (do_pretty_print) (yyval) = list_prepend((yyvsp[0]), instruction(Op_exec_count)); else (yyval) = (yyvsp[0]); } -#line 2552 "awkgram.c" /* yacc.c:1646 */ +#line 2551 "awkgram.c" /* yacc.c:1646 */ break; case 52: -#line 791 "awkgram.y" /* yacc.c:1646 */ +#line 790 "awkgram.y" /* yacc.c:1646 */ { if (! break_allowed) error_ln((yyvsp[-1])->source_line, @@ -2561,11 +2560,11 @@ regular_loop: (yyval) = list_create((yyvsp[-1])); } -#line 2565 "awkgram.c" /* yacc.c:1646 */ +#line 2564 "awkgram.c" /* yacc.c:1646 */ break; case 53: -#line 800 "awkgram.y" /* yacc.c:1646 */ +#line 799 "awkgram.y" /* yacc.c:1646 */ { if (! continue_allowed) error_ln((yyvsp[-1])->source_line, @@ -2574,11 +2573,11 @@ regular_loop: (yyval) = list_create((yyvsp[-1])); } -#line 2578 "awkgram.c" /* yacc.c:1646 */ +#line 2577 "awkgram.c" /* yacc.c:1646 */ break; case 54: -#line 809 "awkgram.y" /* yacc.c:1646 */ +#line 808 "awkgram.y" /* yacc.c:1646 */ { /* if inside function (rule = 0), resolve context at run-time */ if (rule && rule != Rule) @@ -2587,11 +2586,11 @@ regular_loop: (yyvsp[-1])->target_jmp = ip_rec; (yyval) = list_create((yyvsp[-1])); } -#line 2591 "awkgram.c" /* yacc.c:1646 */ +#line 2590 "awkgram.c" /* yacc.c:1646 */ break; case 55: -#line 818 "awkgram.y" /* yacc.c:1646 */ +#line 817 "awkgram.y" /* yacc.c:1646 */ { /* if inside function (rule = 0), resolve context at run-time */ if (rule == BEGIN || rule == END || rule == ENDFILE) @@ -2602,11 +2601,11 @@ regular_loop: (yyvsp[-1])->target_endfile = ip_endfile; (yyval) = list_create((yyvsp[-1])); } -#line 2606 "awkgram.c" /* yacc.c:1646 */ +#line 2605 "awkgram.c" /* yacc.c:1646 */ break; case 56: -#line 829 "awkgram.y" /* yacc.c:1646 */ +#line 828 "awkgram.y" /* yacc.c:1646 */ { /* Initialize the two possible jump targets, the actual target * is resolved at run-time. @@ -2621,20 +2620,20 @@ regular_loop: } else (yyval) = list_append((yyvsp[-1]), (yyvsp[-2])); } -#line 2625 "awkgram.c" /* yacc.c:1646 */ +#line 2624 "awkgram.c" /* yacc.c:1646 */ break; case 57: -#line 844 "awkgram.y" /* yacc.c:1646 */ +#line 843 "awkgram.y" /* yacc.c:1646 */ { if (! in_function) yyerror(_("`return' used outside function context")); } -#line 2634 "awkgram.c" /* yacc.c:1646 */ +#line 2633 "awkgram.c" /* yacc.c:1646 */ break; case 58: -#line 847 "awkgram.y" /* yacc.c:1646 */ +#line 846 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[-1]) == NULL) { (yyval) = list_create((yyvsp[-3])); @@ -2655,17 +2654,17 @@ regular_loop: (yyval) = list_append((yyvsp[-1]), (yyvsp[-3])); } } -#line 2659 "awkgram.c" /* yacc.c:1646 */ +#line 2658 "awkgram.c" /* yacc.c:1646 */ break; case 60: -#line 879 "awkgram.y" /* yacc.c:1646 */ +#line 878 "awkgram.y" /* yacc.c:1646 */ { in_print = true; in_parens = 0; } -#line 2665 "awkgram.c" /* yacc.c:1646 */ +#line 2664 "awkgram.c" /* yacc.c:1646 */ break; case 61: -#line 880 "awkgram.y" /* yacc.c:1646 */ +#line 879 "awkgram.y" /* yacc.c:1646 */ { /* * Optimization: plain `print' has no expression list, so $3 is null. @@ -2762,17 +2761,17 @@ regular_print: } } } -#line 2766 "awkgram.c" /* yacc.c:1646 */ +#line 2765 "awkgram.c" /* yacc.c:1646 */ break; case 62: -#line 977 "awkgram.y" /* yacc.c:1646 */ +#line 976 "awkgram.y" /* yacc.c:1646 */ { sub_counter = 0; } -#line 2772 "awkgram.c" /* yacc.c:1646 */ +#line 2771 "awkgram.c" /* yacc.c:1646 */ break; case 63: -#line 978 "awkgram.y" /* yacc.c:1646 */ +#line 977 "awkgram.y" /* yacc.c:1646 */ { char *arr = (yyvsp[-2])->lextok; @@ -2805,11 +2804,11 @@ regular_print: (yyval) = list_append(list_append((yyvsp[0]), (yyvsp[-2])), (yyvsp[-3])); } } -#line 2809 "awkgram.c" /* yacc.c:1646 */ +#line 2808 "awkgram.c" /* yacc.c:1646 */ break; case 64: -#line 1015 "awkgram.y" /* yacc.c:1646 */ +#line 1014 "awkgram.y" /* yacc.c:1646 */ { static bool warned = false; char *arr = (yyvsp[-1])->lextok; @@ -2835,52 +2834,52 @@ regular_print: fatal(_("`delete' is not allowed with FUNCTAB")); } } -#line 2839 "awkgram.c" /* yacc.c:1646 */ +#line 2838 "awkgram.c" /* yacc.c:1646 */ break; case 65: -#line 1041 "awkgram.y" /* yacc.c:1646 */ +#line 1040 "awkgram.y" /* yacc.c:1646 */ { (yyval) = optimize_assignment((yyvsp[0])); } -#line 2845 "awkgram.c" /* yacc.c:1646 */ +#line 2844 "awkgram.c" /* yacc.c:1646 */ break; case 66: -#line 1046 "awkgram.y" /* yacc.c:1646 */ +#line 1045 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2851 "awkgram.c" /* yacc.c:1646 */ +#line 2850 "awkgram.c" /* yacc.c:1646 */ break; case 67: -#line 1048 "awkgram.y" /* yacc.c:1646 */ +#line 1047 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2857 "awkgram.c" /* yacc.c:1646 */ +#line 2856 "awkgram.c" /* yacc.c:1646 */ break; case 68: -#line 1053 "awkgram.y" /* yacc.c:1646 */ +#line 1052 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2863 "awkgram.c" /* yacc.c:1646 */ +#line 2862 "awkgram.c" /* yacc.c:1646 */ break; case 69: -#line 1055 "awkgram.y" /* yacc.c:1646 */ +#line 1054 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[-1]) == NULL) (yyval) = list_create((yyvsp[0])); else (yyval) = list_prepend((yyvsp[-1]), (yyvsp[0])); } -#line 2874 "awkgram.c" /* yacc.c:1646 */ +#line 2873 "awkgram.c" /* yacc.c:1646 */ break; case 70: -#line 1062 "awkgram.y" /* yacc.c:1646 */ +#line 1061 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 2880 "awkgram.c" /* yacc.c:1646 */ +#line 2879 "awkgram.c" /* yacc.c:1646 */ break; case 71: -#line 1067 "awkgram.y" /* yacc.c:1646 */ +#line 1066 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *casestmt = (yyvsp[0]); if ((yyvsp[0]) == NULL) @@ -2892,11 +2891,11 @@ regular_print: bcfree((yyvsp[-2])); (yyval) = (yyvsp[-4]); } -#line 2896 "awkgram.c" /* yacc.c:1646 */ +#line 2895 "awkgram.c" /* yacc.c:1646 */ break; case 72: -#line 1079 "awkgram.y" /* yacc.c:1646 */ +#line 1078 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *casestmt = (yyvsp[0]); if ((yyvsp[0]) == NULL) @@ -2907,89 +2906,89 @@ regular_print: (yyvsp[-3])->case_stmt = casestmt; (yyval) = (yyvsp[-3]); } -#line 2911 "awkgram.c" /* yacc.c:1646 */ +#line 2910 "awkgram.c" /* yacc.c:1646 */ break; case 73: -#line 1093 "awkgram.y" /* yacc.c:1646 */ +#line 1092 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2917 "awkgram.c" /* yacc.c:1646 */ +#line 2916 "awkgram.c" /* yacc.c:1646 */ break; case 74: -#line 1095 "awkgram.y" /* yacc.c:1646 */ +#line 1094 "awkgram.y" /* yacc.c:1646 */ { NODE *n = (yyvsp[0])->memory; (void) force_number(n); - negate_num(n); + numbr_hndlr->gawk_negate_number(n); bcfree((yyvsp[-1])); (yyval) = (yyvsp[0]); } -#line 2929 "awkgram.c" /* yacc.c:1646 */ +#line 2928 "awkgram.c" /* yacc.c:1646 */ break; case 75: -#line 1103 "awkgram.y" /* yacc.c:1646 */ +#line 1102 "awkgram.y" /* yacc.c:1646 */ { bcfree((yyvsp[-1])); (yyval) = (yyvsp[0]); } -#line 2938 "awkgram.c" /* yacc.c:1646 */ +#line 2937 "awkgram.c" /* yacc.c:1646 */ break; case 76: -#line 1108 "awkgram.y" /* yacc.c:1646 */ +#line 1107 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2944 "awkgram.c" /* yacc.c:1646 */ +#line 2943 "awkgram.c" /* yacc.c:1646 */ break; case 77: -#line 1110 "awkgram.y" /* yacc.c:1646 */ +#line 1109 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_push_re; (yyval) = (yyvsp[0]); } -#line 2953 "awkgram.c" /* yacc.c:1646 */ +#line 2952 "awkgram.c" /* yacc.c:1646 */ break; case 78: -#line 1118 "awkgram.y" /* yacc.c:1646 */ +#line 1117 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2959 "awkgram.c" /* yacc.c:1646 */ +#line 2958 "awkgram.c" /* yacc.c:1646 */ break; case 79: -#line 1120 "awkgram.y" /* yacc.c:1646 */ +#line 1119 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 2965 "awkgram.c" /* yacc.c:1646 */ +#line 2964 "awkgram.c" /* yacc.c:1646 */ break; case 81: -#line 1130 "awkgram.y" /* yacc.c:1646 */ +#line 1129 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 2973 "awkgram.c" /* yacc.c:1646 */ +#line 2972 "awkgram.c" /* yacc.c:1646 */ break; case 82: -#line 1137 "awkgram.y" /* yacc.c:1646 */ +#line 1136 "awkgram.y" /* yacc.c:1646 */ { in_print = false; in_parens = 0; (yyval) = NULL; } -#line 2983 "awkgram.c" /* yacc.c:1646 */ +#line 2982 "awkgram.c" /* yacc.c:1646 */ break; case 83: -#line 1142 "awkgram.y" /* yacc.c:1646 */ +#line 1141 "awkgram.y" /* yacc.c:1646 */ { in_print = false; in_parens = 0; } -#line 2989 "awkgram.c" /* yacc.c:1646 */ +#line 2988 "awkgram.c" /* yacc.c:1646 */ break; case 84: -#line 1143 "awkgram.y" /* yacc.c:1646 */ +#line 1142 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[-2])->redir_type == redirect_twoway && (yyvsp[0])->lasti->opcode == Op_K_getline_redir @@ -2997,136 +2996,136 @@ regular_print: yyerror(_("multistage two-way pipelines don't work")); (yyval) = list_prepend((yyvsp[0]), (yyvsp[-2])); } -#line 3001 "awkgram.c" /* yacc.c:1646 */ +#line 3000 "awkgram.c" /* yacc.c:1646 */ break; case 85: -#line 1154 "awkgram.y" /* yacc.c:1646 */ +#line 1153 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_condition((yyvsp[-3]), (yyvsp[-5]), (yyvsp[0]), NULL, NULL); } -#line 3009 "awkgram.c" /* yacc.c:1646 */ +#line 3008 "awkgram.c" /* yacc.c:1646 */ break; case 86: -#line 1159 "awkgram.y" /* yacc.c:1646 */ +#line 1158 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_condition((yyvsp[-6]), (yyvsp[-8]), (yyvsp[-3]), (yyvsp[-2]), (yyvsp[0])); } -#line 3017 "awkgram.c" /* yacc.c:1646 */ +#line 3016 "awkgram.c" /* yacc.c:1646 */ break; case 91: -#line 1176 "awkgram.y" /* yacc.c:1646 */ +#line 1175 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3023 "awkgram.c" /* yacc.c:1646 */ +#line 3022 "awkgram.c" /* yacc.c:1646 */ break; case 92: -#line 1178 "awkgram.y" /* yacc.c:1646 */ +#line 1177 "awkgram.y" /* yacc.c:1646 */ { bcfree((yyvsp[-1])); (yyval) = (yyvsp[0]); } -#line 3032 "awkgram.c" /* yacc.c:1646 */ +#line 3031 "awkgram.c" /* yacc.c:1646 */ break; case 93: -#line 1186 "awkgram.y" /* yacc.c:1646 */ +#line 1185 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3038 "awkgram.c" /* yacc.c:1646 */ +#line 3037 "awkgram.c" /* yacc.c:1646 */ break; case 94: -#line 1188 "awkgram.y" /* yacc.c:1646 */ +#line 1187 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]) ; } -#line 3044 "awkgram.c" /* yacc.c:1646 */ +#line 3043 "awkgram.c" /* yacc.c:1646 */ break; case 95: -#line 1193 "awkgram.y" /* yacc.c:1646 */ +#line 1192 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->param_count = 0; (yyval) = list_create((yyvsp[0])); } -#line 3053 "awkgram.c" /* yacc.c:1646 */ +#line 3052 "awkgram.c" /* yacc.c:1646 */ break; case 96: -#line 1198 "awkgram.y" /* yacc.c:1646 */ +#line 1197 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->param_count = (yyvsp[-2])->lasti->param_count + 1; (yyval) = list_append((yyvsp[-2]), (yyvsp[0])); yyerrok; } -#line 3063 "awkgram.c" /* yacc.c:1646 */ +#line 3062 "awkgram.c" /* yacc.c:1646 */ break; case 97: -#line 1204 "awkgram.y" /* yacc.c:1646 */ +#line 1203 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3069 "awkgram.c" /* yacc.c:1646 */ +#line 3068 "awkgram.c" /* yacc.c:1646 */ break; case 98: -#line 1206 "awkgram.y" /* yacc.c:1646 */ +#line 1205 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 3075 "awkgram.c" /* yacc.c:1646 */ +#line 3074 "awkgram.c" /* yacc.c:1646 */ break; case 99: -#line 1208 "awkgram.y" /* yacc.c:1646 */ +#line 1207 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-2]); } -#line 3081 "awkgram.c" /* yacc.c:1646 */ +#line 3080 "awkgram.c" /* yacc.c:1646 */ break; case 100: -#line 1214 "awkgram.y" /* yacc.c:1646 */ +#line 1213 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3087 "awkgram.c" /* yacc.c:1646 */ +#line 3086 "awkgram.c" /* yacc.c:1646 */ break; case 101: -#line 1216 "awkgram.y" /* yacc.c:1646 */ +#line 1215 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3093 "awkgram.c" /* yacc.c:1646 */ +#line 3092 "awkgram.c" /* yacc.c:1646 */ break; case 102: -#line 1221 "awkgram.y" /* yacc.c:1646 */ +#line 1220 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3099 "awkgram.c" /* yacc.c:1646 */ +#line 3098 "awkgram.c" /* yacc.c:1646 */ break; case 103: -#line 1223 "awkgram.y" /* yacc.c:1646 */ +#line 1222 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3105 "awkgram.c" /* yacc.c:1646 */ +#line 3104 "awkgram.c" /* yacc.c:1646 */ break; case 104: -#line 1228 "awkgram.y" /* yacc.c:1646 */ +#line 1227 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_expression_list(NULL, (yyvsp[0])); } -#line 3111 "awkgram.c" /* yacc.c:1646 */ +#line 3110 "awkgram.c" /* yacc.c:1646 */ break; case 105: -#line 1230 "awkgram.y" /* yacc.c:1646 */ +#line 1229 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_expression_list((yyvsp[-2]), (yyvsp[0])); yyerrok; } -#line 3120 "awkgram.c" /* yacc.c:1646 */ +#line 3119 "awkgram.c" /* yacc.c:1646 */ break; case 106: -#line 1235 "awkgram.y" /* yacc.c:1646 */ +#line 1234 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3126 "awkgram.c" /* yacc.c:1646 */ +#line 3125 "awkgram.c" /* yacc.c:1646 */ break; case 107: -#line 1237 "awkgram.y" /* yacc.c:1646 */ +#line 1236 "awkgram.y" /* yacc.c:1646 */ { /* * Returning the expression list instead of NULL lets @@ -3134,52 +3133,52 @@ regular_print: */ (yyval) = (yyvsp[-1]); } -#line 3138 "awkgram.c" /* yacc.c:1646 */ +#line 3137 "awkgram.c" /* yacc.c:1646 */ break; case 108: -#line 1245 "awkgram.y" /* yacc.c:1646 */ +#line 1244 "awkgram.y" /* yacc.c:1646 */ { /* Ditto */ (yyval) = mk_expression_list((yyvsp[-2]), (yyvsp[0])); } -#line 3147 "awkgram.c" /* yacc.c:1646 */ +#line 3146 "awkgram.c" /* yacc.c:1646 */ break; case 109: -#line 1250 "awkgram.y" /* yacc.c:1646 */ +#line 1249 "awkgram.y" /* yacc.c:1646 */ { /* Ditto */ (yyval) = (yyvsp[-2]); } -#line 3156 "awkgram.c" /* yacc.c:1646 */ +#line 3155 "awkgram.c" /* yacc.c:1646 */ break; case 110: -#line 1259 "awkgram.y" /* yacc.c:1646 */ +#line 1258 "awkgram.y" /* yacc.c:1646 */ { if (do_lint && (yyvsp[0])->lasti->opcode == Op_match_rec) lintwarn_ln((yyvsp[-1])->source_line, _("regular expression on right of assignment")); (yyval) = mk_assignment((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3167 "awkgram.c" /* yacc.c:1646 */ +#line 3166 "awkgram.c" /* yacc.c:1646 */ break; case 111: -#line 1266 "awkgram.y" /* yacc.c:1646 */ +#line 1265 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_boolean((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3173 "awkgram.c" /* yacc.c:1646 */ +#line 3172 "awkgram.c" /* yacc.c:1646 */ break; case 112: -#line 1268 "awkgram.y" /* yacc.c:1646 */ +#line 1267 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_boolean((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3179 "awkgram.c" /* yacc.c:1646 */ +#line 3178 "awkgram.c" /* yacc.c:1646 */ break; case 113: -#line 1270 "awkgram.y" /* yacc.c:1646 */ +#line 1269 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[-2])->lasti->opcode == Op_match_rec) warning_ln((yyvsp[-1])->source_line, @@ -3195,11 +3194,11 @@ regular_print: (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } } -#line 3199 "awkgram.c" /* yacc.c:1646 */ +#line 3198 "awkgram.c" /* yacc.c:1646 */ break; case 114: -#line 1286 "awkgram.y" /* yacc.c:1646 */ +#line 1285 "awkgram.y" /* yacc.c:1646 */ { if (do_lint_old) warning_ln((yyvsp[-1])->source_line, @@ -3209,91 +3208,91 @@ regular_print: (yyvsp[-1])->expr_count = 1; (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } -#line 3213 "awkgram.c" /* yacc.c:1646 */ +#line 3212 "awkgram.c" /* yacc.c:1646 */ break; case 115: -#line 1296 "awkgram.y" /* yacc.c:1646 */ +#line 1295 "awkgram.y" /* yacc.c:1646 */ { if (do_lint && (yyvsp[0])->lasti->opcode == Op_match_rec) lintwarn_ln((yyvsp[-1])->source_line, _("regular expression on right of comparison")); (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } -#line 3224 "awkgram.c" /* yacc.c:1646 */ +#line 3223 "awkgram.c" /* yacc.c:1646 */ break; case 116: -#line 1303 "awkgram.y" /* yacc.c:1646 */ +#line 1302 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_condition((yyvsp[-4]), (yyvsp[-3]), (yyvsp[-2]), (yyvsp[-1]), (yyvsp[0])); } -#line 3230 "awkgram.c" /* yacc.c:1646 */ +#line 3229 "awkgram.c" /* yacc.c:1646 */ break; case 117: -#line 1305 "awkgram.y" /* yacc.c:1646 */ +#line 1304 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3236 "awkgram.c" /* yacc.c:1646 */ +#line 3235 "awkgram.c" /* yacc.c:1646 */ break; case 118: -#line 1310 "awkgram.y" /* yacc.c:1646 */ +#line 1309 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3242 "awkgram.c" /* yacc.c:1646 */ +#line 3241 "awkgram.c" /* yacc.c:1646 */ break; case 119: -#line 1312 "awkgram.y" /* yacc.c:1646 */ +#line 1311 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3248 "awkgram.c" /* yacc.c:1646 */ +#line 3247 "awkgram.c" /* yacc.c:1646 */ break; case 120: -#line 1314 "awkgram.y" /* yacc.c:1646 */ +#line 1313 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_assign_quotient; (yyval) = (yyvsp[0]); } -#line 3257 "awkgram.c" /* yacc.c:1646 */ +#line 3256 "awkgram.c" /* yacc.c:1646 */ break; case 121: -#line 1322 "awkgram.y" /* yacc.c:1646 */ +#line 1321 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3263 "awkgram.c" /* yacc.c:1646 */ +#line 3262 "awkgram.c" /* yacc.c:1646 */ break; case 122: -#line 1324 "awkgram.y" /* yacc.c:1646 */ +#line 1323 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3269 "awkgram.c" /* yacc.c:1646 */ +#line 3268 "awkgram.c" /* yacc.c:1646 */ break; case 123: -#line 1329 "awkgram.y" /* yacc.c:1646 */ +#line 1328 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3275 "awkgram.c" /* yacc.c:1646 */ +#line 3274 "awkgram.c" /* yacc.c:1646 */ break; case 124: -#line 1331 "awkgram.y" /* yacc.c:1646 */ +#line 1330 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3281 "awkgram.c" /* yacc.c:1646 */ +#line 3280 "awkgram.c" /* yacc.c:1646 */ break; case 125: -#line 1336 "awkgram.y" /* yacc.c:1646 */ +#line 1335 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3287 "awkgram.c" /* yacc.c:1646 */ +#line 3286 "awkgram.c" /* yacc.c:1646 */ break; case 126: -#line 1338 "awkgram.y" /* yacc.c:1646 */ +#line 1337 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3293 "awkgram.c" /* yacc.c:1646 */ +#line 3292 "awkgram.c" /* yacc.c:1646 */ break; case 127: -#line 1340 "awkgram.y" /* yacc.c:1646 */ +#line 1339 "awkgram.y" /* yacc.c:1646 */ { int count = 2; bool is_simple_var = false; @@ -3340,47 +3339,47 @@ regular_print: max_args = count; } } -#line 3344 "awkgram.c" /* yacc.c:1646 */ +#line 3343 "awkgram.c" /* yacc.c:1646 */ break; case 129: -#line 1392 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3350 "awkgram.c" /* yacc.c:1646 */ +#line 1391 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3349 "awkgram.c" /* yacc.c:1646 */ break; case 130: -#line 1394 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3356 "awkgram.c" /* yacc.c:1646 */ +#line 1393 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3355 "awkgram.c" /* yacc.c:1646 */ break; case 131: -#line 1396 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3362 "awkgram.c" /* yacc.c:1646 */ +#line 1395 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3361 "awkgram.c" /* yacc.c:1646 */ break; case 132: -#line 1398 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3368 "awkgram.c" /* yacc.c:1646 */ +#line 1397 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3367 "awkgram.c" /* yacc.c:1646 */ break; case 133: -#line 1400 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3374 "awkgram.c" /* yacc.c:1646 */ +#line 1399 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3373 "awkgram.c" /* yacc.c:1646 */ break; case 134: -#line 1402 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3380 "awkgram.c" /* yacc.c:1646 */ +#line 1401 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3379 "awkgram.c" /* yacc.c:1646 */ break; case 135: -#line 1404 "awkgram.y" /* yacc.c:1646 */ +#line 1403 "awkgram.y" /* yacc.c:1646 */ { /* * In BEGINFILE/ENDFILE, allow `getline [var] < file' @@ -3394,29 +3393,29 @@ regular_print: _("non-redirected `getline' undefined inside END action")); (yyval) = mk_getline((yyvsp[-2]), (yyvsp[-1]), (yyvsp[0]), redirect_input); } -#line 3398 "awkgram.c" /* yacc.c:1646 */ +#line 3397 "awkgram.c" /* yacc.c:1646 */ break; case 136: -#line 1418 "awkgram.y" /* yacc.c:1646 */ +#line 1417 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_postincrement; (yyval) = mk_assignment((yyvsp[-1]), NULL, (yyvsp[0])); } -#line 3407 "awkgram.c" /* yacc.c:1646 */ +#line 3406 "awkgram.c" /* yacc.c:1646 */ break; case 137: -#line 1423 "awkgram.y" /* yacc.c:1646 */ +#line 1422 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_postdecrement; (yyval) = mk_assignment((yyvsp[-1]), NULL, (yyvsp[0])); } -#line 3416 "awkgram.c" /* yacc.c:1646 */ +#line 3415 "awkgram.c" /* yacc.c:1646 */ break; case 138: -#line 1428 "awkgram.y" /* yacc.c:1646 */ +#line 1427 "awkgram.y" /* yacc.c:1646 */ { if (do_lint_old) { warning_ln((yyvsp[-1])->source_line, @@ -3436,64 +3435,64 @@ regular_print: (yyval) = list_append(list_merge(t, (yyvsp[0])), (yyvsp[-1])); } } -#line 3440 "awkgram.c" /* yacc.c:1646 */ +#line 3439 "awkgram.c" /* yacc.c:1646 */ break; case 139: -#line 1453 "awkgram.y" /* yacc.c:1646 */ +#line 1452 "awkgram.y" /* yacc.c:1646 */ { (yyval) = mk_getline((yyvsp[-1]), (yyvsp[0]), (yyvsp[-3]), (yyvsp[-2])->redir_type); bcfree((yyvsp[-2])); } -#line 3449 "awkgram.c" /* yacc.c:1646 */ +#line 3448 "awkgram.c" /* yacc.c:1646 */ break; case 140: -#line 1459 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3455 "awkgram.c" /* yacc.c:1646 */ +#line 1458 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3454 "awkgram.c" /* yacc.c:1646 */ break; case 141: -#line 1461 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3461 "awkgram.c" /* yacc.c:1646 */ +#line 1460 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3460 "awkgram.c" /* yacc.c:1646 */ break; case 142: -#line 1463 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3467 "awkgram.c" /* yacc.c:1646 */ +#line 1462 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3466 "awkgram.c" /* yacc.c:1646 */ break; case 143: -#line 1465 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3473 "awkgram.c" /* yacc.c:1646 */ +#line 1464 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3472 "awkgram.c" /* yacc.c:1646 */ break; case 144: -#line 1467 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3479 "awkgram.c" /* yacc.c:1646 */ +#line 1466 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3478 "awkgram.c" /* yacc.c:1646 */ break; case 145: -#line 1469 "awkgram.y" /* yacc.c:1646 */ - { (yyval) = mk_binary((yyvsp[-2]), (yyvsp[0]), (yyvsp[-1])); } -#line 3485 "awkgram.c" /* yacc.c:1646 */ +#line 1468 "awkgram.y" /* yacc.c:1646 */ + { (yyval) = list_append(list_merge((yyvsp[-2]), (yyvsp[0])), (yyvsp[-1])); } +#line 3484 "awkgram.c" /* yacc.c:1646 */ break; case 146: -#line 1474 "awkgram.y" /* yacc.c:1646 */ +#line 1473 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_create((yyvsp[0])); } -#line 3493 "awkgram.c" /* yacc.c:1646 */ +#line 3492 "awkgram.c" /* yacc.c:1646 */ break; case 147: -#line 1478 "awkgram.y" /* yacc.c:1646 */ +#line 1477 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[0])->opcode == Op_match_rec) { (yyvsp[0])->opcode = Op_nomatch; @@ -3525,37 +3524,37 @@ regular_print: } } } -#line 3529 "awkgram.c" /* yacc.c:1646 */ +#line 3528 "awkgram.c" /* yacc.c:1646 */ break; case 148: -#line 1510 "awkgram.y" /* yacc.c:1646 */ +#line 1509 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 3535 "awkgram.c" /* yacc.c:1646 */ +#line 3534 "awkgram.c" /* yacc.c:1646 */ break; case 149: -#line 1512 "awkgram.y" /* yacc.c:1646 */ +#line 1511 "awkgram.y" /* yacc.c:1646 */ { (yyval) = snode((yyvsp[-1]), (yyvsp[-3])); if ((yyval) == NULL) YYABORT; } -#line 3545 "awkgram.c" /* yacc.c:1646 */ +#line 3544 "awkgram.c" /* yacc.c:1646 */ break; case 150: -#line 1518 "awkgram.y" /* yacc.c:1646 */ +#line 1517 "awkgram.y" /* yacc.c:1646 */ { (yyval) = snode((yyvsp[-1]), (yyvsp[-3])); if ((yyval) == NULL) YYABORT; } -#line 3555 "awkgram.c" /* yacc.c:1646 */ +#line 3554 "awkgram.c" /* yacc.c:1646 */ break; case 151: -#line 1524 "awkgram.y" /* yacc.c:1646 */ +#line 1523 "awkgram.y" /* yacc.c:1646 */ { static bool warned = false; @@ -3568,52 +3567,52 @@ regular_print: if ((yyval) == NULL) YYABORT; } -#line 3572 "awkgram.c" /* yacc.c:1646 */ +#line 3571 "awkgram.c" /* yacc.c:1646 */ break; case 154: -#line 1539 "awkgram.y" /* yacc.c:1646 */ +#line 1538 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[-1])->opcode = Op_preincrement; (yyval) = mk_assignment((yyvsp[0]), NULL, (yyvsp[-1])); } -#line 3581 "awkgram.c" /* yacc.c:1646 */ +#line 3580 "awkgram.c" /* yacc.c:1646 */ break; case 155: -#line 1544 "awkgram.y" /* yacc.c:1646 */ +#line 1543 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[-1])->opcode = Op_predecrement; (yyval) = mk_assignment((yyvsp[0]), NULL, (yyvsp[-1])); } -#line 3590 "awkgram.c" /* yacc.c:1646 */ +#line 3589 "awkgram.c" /* yacc.c:1646 */ break; case 156: -#line 1549 "awkgram.y" /* yacc.c:1646 */ +#line 1548 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_create((yyvsp[0])); } -#line 3598 "awkgram.c" /* yacc.c:1646 */ +#line 3597 "awkgram.c" /* yacc.c:1646 */ break; case 157: -#line 1553 "awkgram.y" /* yacc.c:1646 */ +#line 1552 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_create((yyvsp[0])); } -#line 3606 "awkgram.c" /* yacc.c:1646 */ +#line 3605 "awkgram.c" /* yacc.c:1646 */ break; case 158: -#line 1557 "awkgram.y" /* yacc.c:1646 */ +#line 1556 "awkgram.y" /* yacc.c:1646 */ { if ((yyvsp[0])->lasti->opcode == Op_push_i && ((yyvsp[0])->lasti->memory->flags & (STRCUR|STRING)) == 0 ) { NODE *n = (yyvsp[0])->lasti->memory; (void) force_number(n); - negate_num(n); + numbr_hndlr->gawk_negate_number(n); (yyval) = (yyvsp[0]); bcfree((yyvsp[-1])); } else { @@ -3621,34 +3620,33 @@ regular_print: (yyval) = list_append((yyvsp[0]), (yyvsp[-1])); } } -#line 3625 "awkgram.c" /* yacc.c:1646 */ +#line 3624 "awkgram.c" /* yacc.c:1646 */ break; case 159: -#line 1572 "awkgram.y" /* yacc.c:1646 */ +#line 1571 "awkgram.y" /* yacc.c:1646 */ { /* * was: $$ = $2 * POSIX semantics: force a conversion to numeric type */ - (yyvsp[-1])->opcode = Op_plus_i; - (yyvsp[-1])->memory = make_number(0.0); + (yyvsp[-1])->opcode = Op_unary_plus; (yyval) = list_append((yyvsp[0]), (yyvsp[-1])); } -#line 3639 "awkgram.c" /* yacc.c:1646 */ +#line 3637 "awkgram.c" /* yacc.c:1646 */ break; case 160: -#line 1585 "awkgram.y" /* yacc.c:1646 */ +#line 1583 "awkgram.y" /* yacc.c:1646 */ { func_use((yyvsp[0])->lasti->func_name, FUNC_USE); (yyval) = (yyvsp[0]); } -#line 3648 "awkgram.c" /* yacc.c:1646 */ +#line 3646 "awkgram.c" /* yacc.c:1646 */ break; case 161: -#line 1590 "awkgram.y" /* yacc.c:1646 */ +#line 1588 "awkgram.y" /* yacc.c:1646 */ { /* indirect function call */ INSTRUCTION *f, *t; @@ -3681,11 +3679,11 @@ regular_print: (yyval) = list_prepend((yyvsp[0]), t); } -#line 3685 "awkgram.c" /* yacc.c:1646 */ +#line 3683 "awkgram.c" /* yacc.c:1646 */ break; case 162: -#line 1626 "awkgram.y" /* yacc.c:1646 */ +#line 1624 "awkgram.y" /* yacc.c:1646 */ { param_sanity((yyvsp[-1])); (yyvsp[-3])->opcode = Op_func_call; @@ -3699,49 +3697,49 @@ regular_print: (yyval) = list_append(t, (yyvsp[-3])); } } -#line 3703 "awkgram.c" /* yacc.c:1646 */ +#line 3701 "awkgram.c" /* yacc.c:1646 */ break; case 163: -#line 1643 "awkgram.y" /* yacc.c:1646 */ +#line 1641 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3709 "awkgram.c" /* yacc.c:1646 */ +#line 3707 "awkgram.c" /* yacc.c:1646 */ break; case 164: -#line 1645 "awkgram.y" /* yacc.c:1646 */ +#line 1643 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3715 "awkgram.c" /* yacc.c:1646 */ +#line 3713 "awkgram.c" /* yacc.c:1646 */ break; case 165: -#line 1650 "awkgram.y" /* yacc.c:1646 */ +#line 1648 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3721 "awkgram.c" /* yacc.c:1646 */ +#line 3719 "awkgram.c" /* yacc.c:1646 */ break; case 166: -#line 1652 "awkgram.y" /* yacc.c:1646 */ +#line 1650 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 3727 "awkgram.c" /* yacc.c:1646 */ +#line 3725 "awkgram.c" /* yacc.c:1646 */ break; case 167: -#line 1657 "awkgram.y" /* yacc.c:1646 */ +#line 1655 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3733 "awkgram.c" /* yacc.c:1646 */ +#line 3731 "awkgram.c" /* yacc.c:1646 */ break; case 168: -#line 1659 "awkgram.y" /* yacc.c:1646 */ +#line 1657 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_merge((yyvsp[-1]), (yyvsp[0])); } -#line 3741 "awkgram.c" /* yacc.c:1646 */ +#line 3739 "awkgram.c" /* yacc.c:1646 */ break; case 169: -#line 1666 "awkgram.y" /* yacc.c:1646 */ +#line 1664 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *ip = (yyvsp[0])->lasti; int count = ip->sub_count; /* # of SUBSEP-seperated expressions */ @@ -3755,11 +3753,11 @@ regular_print: sub_counter++; /* count # of dimensions */ (yyval) = (yyvsp[0]); } -#line 3759 "awkgram.c" /* yacc.c:1646 */ +#line 3757 "awkgram.c" /* yacc.c:1646 */ break; case 170: -#line 1683 "awkgram.y" /* yacc.c:1646 */ +#line 1681 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *t = (yyvsp[-1]); if ((yyvsp[-1]) == NULL) { @@ -3773,31 +3771,31 @@ regular_print: (yyvsp[0])->sub_count = count_expressions(&t, false); (yyval) = list_append(t, (yyvsp[0])); } -#line 3777 "awkgram.c" /* yacc.c:1646 */ +#line 3775 "awkgram.c" /* yacc.c:1646 */ break; case 171: -#line 1700 "awkgram.y" /* yacc.c:1646 */ +#line 1698 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); } -#line 3783 "awkgram.c" /* yacc.c:1646 */ +#line 3781 "awkgram.c" /* yacc.c:1646 */ break; case 172: -#line 1702 "awkgram.y" /* yacc.c:1646 */ +#line 1700 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_merge((yyvsp[-1]), (yyvsp[0])); } -#line 3791 "awkgram.c" /* yacc.c:1646 */ +#line 3789 "awkgram.c" /* yacc.c:1646 */ break; case 173: -#line 1709 "awkgram.y" /* yacc.c:1646 */ +#line 1707 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[-1]); } -#line 3797 "awkgram.c" /* yacc.c:1646 */ +#line 3795 "awkgram.c" /* yacc.c:1646 */ break; case 174: -#line 1714 "awkgram.y" /* yacc.c:1646 */ +#line 1712 "awkgram.y" /* yacc.c:1646 */ { char *var_name = (yyvsp[0])->lextok; @@ -3805,22 +3803,22 @@ regular_print: (yyvsp[0])->memory = variable((yyvsp[0])->source_line, var_name, Node_var_new); (yyval) = list_create((yyvsp[0])); } -#line 3809 "awkgram.c" /* yacc.c:1646 */ +#line 3807 "awkgram.c" /* yacc.c:1646 */ break; case 175: -#line 1722 "awkgram.y" /* yacc.c:1646 */ +#line 1720 "awkgram.y" /* yacc.c:1646 */ { char *arr = (yyvsp[-1])->lextok; (yyvsp[-1])->memory = variable((yyvsp[-1])->source_line, arr, Node_var_new); (yyvsp[-1])->opcode = Op_push_array; (yyval) = list_prepend((yyvsp[0]), (yyvsp[-1])); } -#line 3820 "awkgram.c" /* yacc.c:1646 */ +#line 3818 "awkgram.c" /* yacc.c:1646 */ break; case 176: -#line 1732 "awkgram.y" /* yacc.c:1646 */ +#line 1730 "awkgram.y" /* yacc.c:1646 */ { INSTRUCTION *ip = (yyvsp[0])->nexti; if (ip->opcode == Op_push @@ -3832,73 +3830,73 @@ regular_print: } else (yyval) = (yyvsp[0]); } -#line 3836 "awkgram.c" /* yacc.c:1646 */ +#line 3834 "awkgram.c" /* yacc.c:1646 */ break; case 177: -#line 1744 "awkgram.y" /* yacc.c:1646 */ +#line 1742 "awkgram.y" /* yacc.c:1646 */ { (yyval) = list_append((yyvsp[-1]), (yyvsp[-2])); if ((yyvsp[0]) != NULL) mk_assignment((yyvsp[-1]), NULL, (yyvsp[0])); } -#line 3846 "awkgram.c" /* yacc.c:1646 */ +#line 3844 "awkgram.c" /* yacc.c:1646 */ break; case 178: -#line 1753 "awkgram.y" /* yacc.c:1646 */ +#line 1751 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_postincrement; } -#line 3854 "awkgram.c" /* yacc.c:1646 */ +#line 3852 "awkgram.c" /* yacc.c:1646 */ break; case 179: -#line 1757 "awkgram.y" /* yacc.c:1646 */ +#line 1755 "awkgram.y" /* yacc.c:1646 */ { (yyvsp[0])->opcode = Op_postdecrement; } -#line 3862 "awkgram.c" /* yacc.c:1646 */ +#line 3860 "awkgram.c" /* yacc.c:1646 */ break; case 180: -#line 1760 "awkgram.y" /* yacc.c:1646 */ +#line 1758 "awkgram.y" /* yacc.c:1646 */ { (yyval) = NULL; } -#line 3868 "awkgram.c" /* yacc.c:1646 */ +#line 3866 "awkgram.c" /* yacc.c:1646 */ break; case 182: -#line 1768 "awkgram.y" /* yacc.c:1646 */ +#line 1766 "awkgram.y" /* yacc.c:1646 */ { yyerrok; } -#line 3874 "awkgram.c" /* yacc.c:1646 */ +#line 3872 "awkgram.c" /* yacc.c:1646 */ break; case 183: -#line 1772 "awkgram.y" /* yacc.c:1646 */ +#line 1770 "awkgram.y" /* yacc.c:1646 */ { yyerrok; } -#line 3880 "awkgram.c" /* yacc.c:1646 */ +#line 3878 "awkgram.c" /* yacc.c:1646 */ break; case 186: -#line 1781 "awkgram.y" /* yacc.c:1646 */ +#line 1779 "awkgram.y" /* yacc.c:1646 */ { yyerrok; } -#line 3886 "awkgram.c" /* yacc.c:1646 */ +#line 3884 "awkgram.c" /* yacc.c:1646 */ break; case 187: -#line 1785 "awkgram.y" /* yacc.c:1646 */ +#line 1783 "awkgram.y" /* yacc.c:1646 */ { (yyval) = (yyvsp[0]); yyerrok; } -#line 3892 "awkgram.c" /* yacc.c:1646 */ +#line 3890 "awkgram.c" /* yacc.c:1646 */ break; case 188: -#line 1789 "awkgram.y" /* yacc.c:1646 */ +#line 1787 "awkgram.y" /* yacc.c:1646 */ { yyerrok; } -#line 3898 "awkgram.c" /* yacc.c:1646 */ +#line 3896 "awkgram.c" /* yacc.c:1646 */ break; -#line 3902 "awkgram.c" /* yacc.c:1646 */ +#line 3900 "awkgram.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -4126,7 +4124,7 @@ yyreturn: #endif return yyresult; } -#line 1791 "awkgram.y" /* yacc.c:1906 */ +#line 1789 "awkgram.y" /* yacc.c:1906 */ struct token { @@ -4143,9 +4141,9 @@ struct token { # define BREAK 0x0800 /* break allowed inside */ # define CONTINUE 0x1000 /* continue allowed inside */ # define DEBUG_USE 0x2000 /* for use by developers */ +# define ARG3_IS_ARR 0x4000 /* hack for div/do_mpfp_div */ NODE *(*ptr)(int); /* function that implements this keyword */ - NODE *(*ptr2)(int); /* alternate arbitrary-precision function */ }; #if 'a' == 0x81 /* it's EBCDIC */ @@ -4169,91 +4167,86 @@ tokcompare(const void *l, const void *r) * Function pointers come from declarations in awk.h. */ -#ifdef HAVE_MPFR -#define MPF(F) do_mpfr_##F -#else -#define MPF(F) 0 -#endif -static const struct token tokentab[] = { -{"BEGIN", Op_rule, LEX_BEGIN, 0, 0, 0}, -{"BEGINFILE", Op_rule, LEX_BEGINFILE, GAWKX, 0, 0}, -{"END", Op_rule, LEX_END, 0, 0, 0}, -{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0, 0}, +static struct token tokentab[] = { +{"BEGIN", Op_rule, LEX_BEGIN, 0, 0 }, +{"BEGINFILE", Op_rule, LEX_BEGINFILE, GAWKX, 0 }, +{"END", Op_rule, LEX_END, 0, 0 }, +{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0 }, #ifdef ARRAYDEBUG -{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|DEBUG_USE, do_adump, 0}, +{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|DEBUG_USE, do_adump }, #endif -{"and", Op_builtin, LEX_BUILTIN, GAWKX, do_and, MPF(and)}, -{"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort, 0}, -{"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti, 0}, -{"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2, MPF(atan2)}, -{"bindtextdomain", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_bindtextdomain, 0}, -{"break", Op_K_break, LEX_BREAK, 0, 0, 0}, -{"case", Op_K_case, LEX_CASE, GAWKX, 0, 0}, -{"close", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1)|A(2), do_close, 0}, -{"compl", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_compl, MPF(compl)}, -{"continue", Op_K_continue, LEX_CONTINUE, 0, 0, 0}, -{"cos", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_cos, MPF(cos)}, -{"dcgettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_dcgettext, 0}, -{"dcngettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), do_dcngettext, 0}, -{"default", Op_K_default, LEX_DEFAULT, GAWKX, 0, 0}, -{"delete", Op_K_delete, LEX_DELETE, NOT_OLD, 0, 0}, -{"div", Op_builtin, LEX_BUILTIN, GAWKX|A(3), do_div, MPF(div)}, -{"do", Op_K_do, LEX_DO, NOT_OLD|BREAK|CONTINUE, 0, 0}, -{"else", Op_K_else, LEX_ELSE, 0, 0, 0}, -{"eval", Op_symbol, LEX_EVAL, 0, 0, 0}, -{"exit", Op_K_exit, LEX_EXIT, 0, 0, 0}, -{"exp", Op_builtin, LEX_BUILTIN, A(1), do_exp, MPF(exp)}, +{"and", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, +{"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort }, +{"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti }, +{"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), 0 }, +{"bindtextdomain", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_bindtextdomain }, +{"break", Op_K_break, LEX_BREAK, 0, 0 }, +{"case", Op_K_case, LEX_CASE, GAWKX, 0 }, +{"close", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1)|A(2), do_close }, +{"compl", Op_builtin, LEX_BUILTIN, GAWKX|A(1), 0 }, +{"continue", Op_K_continue, LEX_CONTINUE, 0, 0 }, +{"cos", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), 0 }, +{"dcgettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_dcgettext }, +{"dcngettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), do_dcngettext }, +{"default", Op_K_default, LEX_DEFAULT, GAWKX, 0 }, +{"delete", Op_K_delete, LEX_DELETE, NOT_OLD, 0 }, +{"div", Op_builtin, LEX_BUILTIN, GAWKX|A(3)|ARG3_IS_ARR, do_div }, +{"do", Op_K_do, LEX_DO, NOT_OLD|BREAK|CONTINUE, 0 }, +{"else", Op_K_else, LEX_ELSE, 0, 0 }, +{"eval", Op_symbol, LEX_EVAL, 0, 0 }, +{"exit", Op_K_exit, LEX_EXIT, 0, 0 }, +{"exp", Op_builtin, LEX_BUILTIN, A(1), 0 }, #ifdef DYNAMIC -{"extension", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_ext, 0}, +{"extension", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_ext }, #endif -{"fflush", Op_builtin, LEX_BUILTIN, A(0)|A(1), do_fflush, 0}, -{"for", Op_K_for, LEX_FOR, BREAK|CONTINUE, 0, 0}, -{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0, 0}, -{"function",Op_func, LEX_FUNCTION, NOT_OLD, 0, 0}, -{"gensub", Op_sub_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), 0, 0}, -{"getline", Op_K_getline_redir, LEX_GETLINE, NOT_OLD, 0, 0}, -{"gsub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0, 0}, -{"if", Op_K_if, LEX_IF, 0, 0, 0}, -{"in", Op_symbol, LEX_IN, 0, 0, 0}, -{"include", Op_symbol, LEX_INCLUDE, GAWKX, 0, 0}, -{"index", Op_builtin, LEX_BUILTIN, A(2), do_index, 0}, -{"int", Op_builtin, LEX_BUILTIN, A(1), do_int, MPF(int)}, -{"isarray", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_isarray, 0}, -{"length", Op_builtin, LEX_LENGTH, A(0)|A(1), do_length, 0}, -{"load", Op_symbol, LEX_LOAD, GAWKX, 0, 0}, -{"log", Op_builtin, LEX_BUILTIN, A(1), do_log, MPF(log)}, -{"lshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_lshift, MPF(lshift)}, -{"match", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match, 0}, -{"mktime", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_mktime, 0}, -{"next", Op_K_next, LEX_NEXT, 0, 0, 0}, -{"nextfile", Op_K_nextfile, LEX_NEXTFILE, 0, 0, 0}, -{"or", Op_builtin, LEX_BUILTIN, GAWKX, do_or, MPF(or)}, -{"patsplit", Op_builtin, LEX_BUILTIN, GAWKX|A(2)|A(3)|A(4), do_patsplit, 0}, -{"print", Op_K_print, LEX_PRINT, 0, 0, 0}, -{"printf", Op_K_printf, LEX_PRINTF, 0, 0, 0}, -{"rand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0), do_rand, MPF(rand)}, -{"return", Op_K_return, LEX_RETURN, NOT_OLD, 0, 0}, -{"rshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_rshift, MPF(rshift)}, -{"sin", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_sin, MPF(sin)}, -{"split", Op_builtin, LEX_BUILTIN, A(2)|A(3)|A(4), do_split, 0}, -{"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf, 0}, -{"sqrt", Op_builtin, LEX_BUILTIN, A(1), do_sqrt, MPF(sqrt)}, -{"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand, MPF(srand)}, +{"fflush", Op_builtin, LEX_BUILTIN, A(0)|A(1), do_fflush }, +{"for", Op_K_for, LEX_FOR, BREAK|CONTINUE, 0 }, +{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0 }, +{"function",Op_func, LEX_FUNCTION, NOT_OLD, 0 }, +{"gensub", Op_sub_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), 0 }, +{"getline", Op_K_getline_redir, LEX_GETLINE, NOT_OLD, 0 }, +{"gsub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0 }, +{"if", Op_K_if, LEX_IF, 0, 0 }, +{"in", Op_symbol, LEX_IN, 0, 0 }, +{"include", Op_symbol, LEX_INCLUDE, GAWKX, 0 }, +{"index", Op_builtin, LEX_BUILTIN, A(2), do_index }, +{"int", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"isarray", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_isarray }, +{"length", Op_builtin, LEX_LENGTH, A(0)|A(1), do_length }, +{"load", Op_symbol, LEX_LOAD, GAWKX, 0 }, +{"log", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"lshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), 0 }, +{"match", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match }, +{"mktime", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_mktime }, +{"next", Op_K_next, LEX_NEXT, 0, 0 }, +{"nextfile", Op_K_nextfile, LEX_NEXTFILE, 0, 0 }, +{"or", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, +{"patsplit", Op_builtin, LEX_BUILTIN, GAWKX|A(2)|A(3)|A(4), do_patsplit }, +{"print", Op_K_print, LEX_PRINT, 0, 0 }, +{"printf", Op_K_printf, LEX_PRINTF, 0, 0 }, +{"rand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0), 0 }, +{"return", Op_K_return, LEX_RETURN, NOT_OLD, 0 }, +{"rshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), 0 }, +{"sin", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), 0 }, +{"split", Op_builtin, LEX_BUILTIN, A(2)|A(3)|A(4), do_split }, +{"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf }, +{"sqrt", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), 0 }, #if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */ -{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|DEBUG_USE, stopme, 0}, +{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|DEBUG_USE, stopme }, #endif -{"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime, 0}, -{"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_strtonum, MPF(strtonum)}, -{"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0, 0}, -{"substr", Op_builtin, LEX_BUILTIN, A(2)|A(3), do_substr, 0}, -{"switch", Op_K_switch, LEX_SWITCH, GAWKX|BREAK, 0, 0}, -{"system", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system, 0}, -{"systime", Op_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime, 0}, -{"tolower", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower, 0}, -{"toupper", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper, 0}, -{"while", Op_K_while, LEX_WHILE, BREAK|CONTINUE, 0, 0}, -{"xor", Op_builtin, LEX_BUILTIN, GAWKX, do_xor, MPF(xor)}, +{"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime }, +{"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), 0 }, +{"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0 }, +{"substr", Op_builtin, LEX_BUILTIN, A(2)|A(3), do_substr }, +{"switch", Op_K_switch, LEX_SWITCH, GAWKX|BREAK, 0 }, +{"system", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system }, +{"systime", Op_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime }, +{"tolower", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower }, +{"toupper", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper }, +{"while", Op_K_while, LEX_WHILE, BREAK|CONTINUE, 0 }, +{"xor", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, }; #if MBS_SUPPORT @@ -4283,53 +4276,12 @@ getfname(NODE *(*fptr)(int)) j = sizeof(tokentab) / sizeof(tokentab[0]); /* linear search, no other way to do it */ for (i = 0; i < j; i++) - if (tokentab[i].ptr == fptr || tokentab[i].ptr2 == fptr) + if (tokentab[i].ptr == fptr) return tokentab[i].operator; return NULL; } -/* negate_num --- negate a number in NODE */ - -void -negate_num(NODE *n) -{ -#ifdef HAVE_MPFR - int tval = 0; -#endif - - if (! is_mpg_number(n)) { - n->numbr = -n->numbr; - return; - } - -#ifdef HAVE_MPFR - if (is_mpg_integer(n)) { - if (! iszero(n)) { - mpz_neg(n->mpg_i, n->mpg_i); - return; - } - - /* - * 0 --> -0 conversion. Requires turning the MPG integer - * into an MPFR float. - */ - - mpz_clear(n->mpg_i); /* release the integer storage */ - - /* Convert and fall through. */ - tval = mpfr_set_d(n->mpg_numbr, 0.0, ROUND_MODE); - IEEE_FMT(n->mpg_numbr, tval); - n->flags &= ~MPZN; - n->flags |= MPFN; - } - - /* mpfr float case */ - tval = mpfr_neg(n->mpg_numbr, n->mpg_numbr, ROUND_MODE); - IEEE_FMT(n->mpg_numbr, tval); -#endif -} - /* print_included_from --- print `Included from ..' file names and locations */ static void @@ -4498,6 +4450,18 @@ yyerror(const char *m, ...) efree(buf); } +void +init_parser(const bltin_t *numbr_bltins) +{ + int i, tokentab_idx; + + for (i = 0; numbr_bltins[i].name != NULL; i++) { + tokentab_idx = check_special(numbr_bltins[i].name); + tokentab[tokentab_idx].ptr = numbr_bltins[i].ptr; + } +} + + /* mk_program --- create a single list of instructions */ static INSTRUCTION * @@ -5336,7 +5300,6 @@ yylex(void) char *tokkey; bool inhex = false; bool intlstr = false; - AWKNUM d; #define GET_INSTRUCTION(op) bcalloc(op, 1, sourceline) @@ -5914,32 +5877,7 @@ retry: } } -#ifdef HAVE_MPFR - if (do_mpfr) { - NODE *r; - - if (! seen_point && ! seen_e) { - r = mpg_integer(); - mpg_strtoui(r->mpg_i, tokstart, strlen(tokstart), NULL, base); - errno = 0; - } else { - int tval; - r = mpg_float(); - tval = mpfr_strtofr(r->mpg_numbr, tokstart, NULL, base, ROUND_MODE); - errno = 0; - IEEE_FMT(r->mpg_numbr, tval); - } - yylval->memory = r; - return lasttok = YNUMBER; - } -#endif - if (base != 10) - d = nondec2awknum(tokstart, strlen(tokstart)); - else - d = atof(tokstart); - yylval->memory = make_number(d); - if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) - yylval->memory->flags |= NUMINT; + yylval->memory = str2node(tokstart, NULL, base, ! (seen_point || seen_e)); return lasttok = YNUMBER; case '&': @@ -6243,13 +6181,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) } } -#ifdef HAVE_MPFR - /* N.B.: If necessary, add special processing for alternate builtin, below */ - if (do_mpfr && tokentab[idx].ptr2) - r->builtin = tokentab[idx].ptr2; - else -#endif - r->builtin = tokentab[idx].ptr; + r->builtin = tokentab[idx].ptr; /* special case processing for a few builtins */ @@ -6273,11 +6205,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) arg = subn->nexti; if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push) arg->nexti->opcode = Op_push_arg; /* argument may be array */ - } else if (r->builtin == do_div -#ifdef HAVE_MPFR - || r->builtin == MPF(div) -#endif - ) { + } else if ((tokentab[idx].flags & ARG3_IS_ARR) != 0) { arg = subn->nexti->lasti->nexti->lasti->nexti; /* 3rd arg list */ ip = arg->lasti; if (ip->opcode == Op_push) @@ -6471,26 +6399,12 @@ valinfo(NODE *n, Func_print print_func, FILE *fp) pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false); print_func(fp, "\n"); } else if (n->flags & NUMBER) { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - print_func(fp, "%.17g\n", n->numbr); + print_func(fp, "%s\n", fmt_number("%.17g", n)); } else if (n->flags & STRCUR) { pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false); print_func(fp, "\n"); } else if (n->flags & NUMCUR) { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - print_func(fp, "%.17g\n", n->numbr); + print_func(fp, "%s\n", fmt_number("%.17g", n)); } else print_func(fp, "?? flags %s\n", flags2str(n->flags)); } @@ -6958,20 +6872,15 @@ isnoeffect(OPCODE type) { switch (type) { case Op_times: - case Op_times_i: case Op_quotient: - case Op_quotient_i: case Op_mod: - case Op_mod_i: case Op_plus: - case Op_plus_i: case Op_minus: - case Op_minus_i: case Op_subscript: case Op_concat: case Op_exp: - case Op_exp_i: case Op_unary_minus: + case Op_unary_plus: case Op_field_spec: case Op_and_final: case Op_or_final: @@ -7074,113 +6983,6 @@ dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2) fflush(stdout); } -/* mk_binary --- instructions for binary operators */ - -static INSTRUCTION * -mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op) -{ - INSTRUCTION *ip1,*ip2; - AWKNUM res; - - ip2 = s2->nexti; - if (s2->lasti == ip2 && ip2->opcode == Op_push_i) { - /* do any numeric constant folding */ - ip1 = s1->nexti; - if (do_optimize - && ip1 == s1->lasti && ip1->opcode == Op_push_i - && (ip1->memory->flags & (MPFN|MPZN|STRCUR|STRING)) == 0 - && (ip2->memory->flags & (MPFN|MPZN|STRCUR|STRING)) == 0 - ) { - NODE *n1 = ip1->memory, *n2 = ip2->memory; - res = force_number(n1)->numbr; - (void) force_number(n2); - switch (op->opcode) { - case Op_times: - res *= n2->numbr; - break; - case Op_quotient: - if (n2->numbr == 0.0) { - /* don't fatalize, allow parsing rest of the input */ - error_ln(op->source_line, _("division by zero attempted")); - goto regular; - } - - res /= n2->numbr; - break; - case Op_mod: - if (n2->numbr == 0.0) { - /* don't fatalize, allow parsing rest of the input */ - error_ln(op->source_line, _("division by zero attempted in `%%'")); - goto regular; - } -#ifdef HAVE_FMOD - res = fmod(res, n2->numbr); -#else /* ! HAVE_FMOD */ - (void) modf(res / n2->numbr, &res); - res = n1->numbr - res * n2->numbr; -#endif /* ! HAVE_FMOD */ - break; - case Op_plus: - res += n2->numbr; - break; - case Op_minus: - res -= n2->numbr; - break; - case Op_exp: - res = calc_exp(res, n2->numbr); - break; - default: - goto regular; - } - - op->opcode = Op_push_i; - op->memory = make_number(res); - unref(n1); - unref(n2); - bcfree(ip1); - bcfree(ip2); - bcfree(s1); - bcfree(s2); - return list_create(op); - } else { - /* do basic arithmetic optimisation */ - /* convert (Op_push_i Node_val) + (Op_plus) to (Op_plus_i Node_val) */ - switch (op->opcode) { - case Op_times: - op->opcode = Op_times_i; - break; - case Op_quotient: - op->opcode = Op_quotient_i; - break; - case Op_mod: - op->opcode = Op_mod_i; - break; - case Op_plus: - op->opcode = Op_plus_i; - break; - case Op_minus: - op->opcode = Op_minus_i; - break; - case Op_exp: - op->opcode = Op_exp_i; - break; - default: - goto regular; - } - - op->memory = ip2->memory; - bcfree(ip2); - bcfree(s2); /* Op_list */ - return list_append(s1, op); - } - } - -regular: - /* append lists s1, s2 and add `op' bytecode */ - (void) list_merge(s1, s2); - return list_append(s1, op); -} - /* mk_boolean --- instructions for boolean and, or */ static INSTRUCTION * @@ -7578,8 +7380,8 @@ optimize_assignment(INSTRUCTION *exp) i3->opcode = Op_store_sub; i3->memory = i2->memory; i3->expr_count = 1; /* sub_count shadows memory, - * so use expr_count instead. - */ + * so use expr_count instead + */ i3->nexti = NULL; i2->opcode = Op_no_op; bcfree(i1); /* Op_assign */ @@ -8062,10 +7864,6 @@ lookup_builtin(const char *name) if (mid == -1 || tokentab[mid].class != LEX_BUILTIN) return NULL; -#ifdef HAVE_MPFR - if (do_mpfr) - return tokentab[mid].ptr2; -#endif return tokentab[mid].ptr; } @@ -70,7 +70,6 @@ static INSTRUCTION *mk_expression_list(INSTRUCTION *list, INSTRUCTION *s1); static INSTRUCTION *mk_for_loop(INSTRUCTION *forp, INSTRUCTION *init, INSTRUCTION *cond, INSTRUCTION *incr, INSTRUCTION *body); static void fix_break_continue(INSTRUCTION *list, INSTRUCTION *b_target, INSTRUCTION *c_target); -static INSTRUCTION *mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op); static INSTRUCTION *mk_boolean(INSTRUCTION *left, INSTRUCTION *right, INSTRUCTION *op); static INSTRUCTION *mk_assignment(INSTRUCTION *lhs, INSTRUCTION *rhs, INSTRUCTION *op); static INSTRUCTION *mk_getline(INSTRUCTION *op, INSTRUCTION *opt_var, INSTRUCTION *redir, int redirtype); @@ -1095,7 +1094,7 @@ case_value { NODE *n = $2->memory; (void) force_number(n); - negate_num(n); + numbr_hndlr->gawk_negate_number(n); bcfree($1); $$ = $2; } @@ -1389,17 +1388,17 @@ simp_exp : non_post_simp_exp /* Binary operators in order of decreasing precedence. */ | simp_exp '^' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp '*' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp '/' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp '%' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp '+' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp '-' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | LEX_GETLINE opt_variable input_redir { /* @@ -1456,17 +1455,17 @@ simp_exp_nc } /* Binary operators in order of decreasing precedence. */ | simp_exp_nc '^' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp_nc '*' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp_nc '/' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp_nc '%' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp_nc '+' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } | simp_exp_nc '-' simp_exp - { $$ = mk_binary($1, $3, $2); } + { $$ = list_append(list_merge($1, $3), $2); } ; non_post_simp_exp @@ -1560,7 +1559,7 @@ non_post_simp_exp ) { NODE *n = $2->lasti->memory; (void) force_number(n); - negate_num(n); + numbr_hndlr->gawk_negate_number(n); $$ = $2; bcfree($1); } else { @@ -1574,8 +1573,7 @@ non_post_simp_exp * was: $$ = $2 * POSIX semantics: force a conversion to numeric type */ - $1->opcode = Op_plus_i; - $1->memory = make_number(0.0); + $1->opcode = Op_unary_plus; $$ = list_append($2, $1); } ; @@ -1804,9 +1802,9 @@ struct token { # define BREAK 0x0800 /* break allowed inside */ # define CONTINUE 0x1000 /* continue allowed inside */ # define DEBUG_USE 0x2000 /* for use by developers */ +# define ARG3_IS_ARR 0x4000 /* hack for div/do_mpfp_div */ NODE *(*ptr)(int); /* function that implements this keyword */ - NODE *(*ptr2)(int); /* alternate arbitrary-precision function */ }; #if 'a' == 0x81 /* it's EBCDIC */ @@ -1830,91 +1828,86 @@ tokcompare(const void *l, const void *r) * Function pointers come from declarations in awk.h. */ -#ifdef HAVE_MPFR -#define MPF(F) do_mpfr_##F -#else -#define MPF(F) 0 -#endif -static const struct token tokentab[] = { -{"BEGIN", Op_rule, LEX_BEGIN, 0, 0, 0}, -{"BEGINFILE", Op_rule, LEX_BEGINFILE, GAWKX, 0, 0}, -{"END", Op_rule, LEX_END, 0, 0, 0}, -{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0, 0}, +static struct token tokentab[] = { +{"BEGIN", Op_rule, LEX_BEGIN, 0, 0 }, +{"BEGINFILE", Op_rule, LEX_BEGINFILE, GAWKX, 0 }, +{"END", Op_rule, LEX_END, 0, 0 }, +{"ENDFILE", Op_rule, LEX_ENDFILE, GAWKX, 0 }, #ifdef ARRAYDEBUG -{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|DEBUG_USE, do_adump, 0}, +{"adump", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|DEBUG_USE, do_adump }, #endif -{"and", Op_builtin, LEX_BUILTIN, GAWKX, do_and, MPF(and)}, -{"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort, 0}, -{"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti, 0}, -{"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2, MPF(atan2)}, -{"bindtextdomain", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_bindtextdomain, 0}, -{"break", Op_K_break, LEX_BREAK, 0, 0, 0}, -{"case", Op_K_case, LEX_CASE, GAWKX, 0, 0}, -{"close", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1)|A(2), do_close, 0}, -{"compl", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_compl, MPF(compl)}, -{"continue", Op_K_continue, LEX_CONTINUE, 0, 0, 0}, -{"cos", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_cos, MPF(cos)}, -{"dcgettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_dcgettext, 0}, -{"dcngettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), do_dcngettext, 0}, -{"default", Op_K_default, LEX_DEFAULT, GAWKX, 0, 0}, -{"delete", Op_K_delete, LEX_DELETE, NOT_OLD, 0, 0}, -{"div", Op_builtin, LEX_BUILTIN, GAWKX|A(3), do_div, MPF(div)}, -{"do", Op_K_do, LEX_DO, NOT_OLD|BREAK|CONTINUE, 0, 0}, -{"else", Op_K_else, LEX_ELSE, 0, 0, 0}, -{"eval", Op_symbol, LEX_EVAL, 0, 0, 0}, -{"exit", Op_K_exit, LEX_EXIT, 0, 0, 0}, -{"exp", Op_builtin, LEX_BUILTIN, A(1), do_exp, MPF(exp)}, +{"and", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, +{"asort", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asort }, +{"asorti", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_asorti }, +{"atan2", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2), 0 }, +{"bindtextdomain", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_bindtextdomain }, +{"break", Op_K_break, LEX_BREAK, 0, 0 }, +{"case", Op_K_case, LEX_CASE, GAWKX, 0 }, +{"close", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1)|A(2), do_close }, +{"compl", Op_builtin, LEX_BUILTIN, GAWKX|A(1), 0 }, +{"continue", Op_K_continue, LEX_CONTINUE, 0, 0 }, +{"cos", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), 0 }, +{"dcgettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_dcgettext }, +{"dcngettext", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3)|A(4)|A(5), do_dcngettext }, +{"default", Op_K_default, LEX_DEFAULT, GAWKX, 0 }, +{"delete", Op_K_delete, LEX_DELETE, NOT_OLD, 0 }, +{"div", Op_builtin, LEX_BUILTIN, GAWKX|A(3)|ARG3_IS_ARR, do_div }, +{"do", Op_K_do, LEX_DO, NOT_OLD|BREAK|CONTINUE, 0 }, +{"else", Op_K_else, LEX_ELSE, 0, 0 }, +{"eval", Op_symbol, LEX_EVAL, 0, 0 }, +{"exit", Op_K_exit, LEX_EXIT, 0, 0 }, +{"exp", Op_builtin, LEX_BUILTIN, A(1), 0 }, #ifdef DYNAMIC -{"extension", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_ext, 0}, +{"extension", Op_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_ext }, #endif -{"fflush", Op_builtin, LEX_BUILTIN, A(0)|A(1), do_fflush, 0}, -{"for", Op_K_for, LEX_FOR, BREAK|CONTINUE, 0, 0}, -{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0, 0}, -{"function",Op_func, LEX_FUNCTION, NOT_OLD, 0, 0}, -{"gensub", Op_sub_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), 0, 0}, -{"getline", Op_K_getline_redir, LEX_GETLINE, NOT_OLD, 0, 0}, -{"gsub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0, 0}, -{"if", Op_K_if, LEX_IF, 0, 0, 0}, -{"in", Op_symbol, LEX_IN, 0, 0, 0}, -{"include", Op_symbol, LEX_INCLUDE, GAWKX, 0, 0}, -{"index", Op_builtin, LEX_BUILTIN, A(2), do_index, 0}, -{"int", Op_builtin, LEX_BUILTIN, A(1), do_int, MPF(int)}, -{"isarray", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_isarray, 0}, -{"length", Op_builtin, LEX_LENGTH, A(0)|A(1), do_length, 0}, -{"load", Op_symbol, LEX_LOAD, GAWKX, 0, 0}, -{"log", Op_builtin, LEX_BUILTIN, A(1), do_log, MPF(log)}, -{"lshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_lshift, MPF(lshift)}, -{"match", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match, 0}, -{"mktime", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_mktime, 0}, -{"next", Op_K_next, LEX_NEXT, 0, 0, 0}, -{"nextfile", Op_K_nextfile, LEX_NEXTFILE, 0, 0, 0}, -{"or", Op_builtin, LEX_BUILTIN, GAWKX, do_or, MPF(or)}, -{"patsplit", Op_builtin, LEX_BUILTIN, GAWKX|A(2)|A(3)|A(4), do_patsplit, 0}, -{"print", Op_K_print, LEX_PRINT, 0, 0, 0}, -{"printf", Op_K_printf, LEX_PRINTF, 0, 0, 0}, -{"rand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0), do_rand, MPF(rand)}, -{"return", Op_K_return, LEX_RETURN, NOT_OLD, 0, 0}, -{"rshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), do_rshift, MPF(rshift)}, -{"sin", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_sin, MPF(sin)}, -{"split", Op_builtin, LEX_BUILTIN, A(2)|A(3)|A(4), do_split, 0}, -{"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf, 0}, -{"sqrt", Op_builtin, LEX_BUILTIN, A(1), do_sqrt, MPF(sqrt)}, -{"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand, MPF(srand)}, +{"fflush", Op_builtin, LEX_BUILTIN, A(0)|A(1), do_fflush }, +{"for", Op_K_for, LEX_FOR, BREAK|CONTINUE, 0 }, +{"func", Op_func, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0 }, +{"function",Op_func, LEX_FUNCTION, NOT_OLD, 0 }, +{"gensub", Op_sub_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), 0 }, +{"getline", Op_K_getline_redir, LEX_GETLINE, NOT_OLD, 0 }, +{"gsub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0 }, +{"if", Op_K_if, LEX_IF, 0, 0 }, +{"in", Op_symbol, LEX_IN, 0, 0 }, +{"include", Op_symbol, LEX_INCLUDE, GAWKX, 0 }, +{"index", Op_builtin, LEX_BUILTIN, A(2), do_index }, +{"int", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"isarray", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_isarray }, +{"length", Op_builtin, LEX_LENGTH, A(0)|A(1), do_length }, +{"load", Op_symbol, LEX_LOAD, GAWKX, 0 }, +{"log", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"lshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), 0 }, +{"match", Op_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match }, +{"mktime", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_mktime }, +{"next", Op_K_next, LEX_NEXT, 0, 0 }, +{"nextfile", Op_K_nextfile, LEX_NEXTFILE, 0, 0 }, +{"or", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, +{"patsplit", Op_builtin, LEX_BUILTIN, GAWKX|A(2)|A(3)|A(4), do_patsplit }, +{"print", Op_K_print, LEX_PRINT, 0, 0 }, +{"printf", Op_K_printf, LEX_PRINTF, 0, 0 }, +{"rand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0), 0 }, +{"return", Op_K_return, LEX_RETURN, NOT_OLD, 0 }, +{"rshift", Op_builtin, LEX_BUILTIN, GAWKX|A(2), 0 }, +{"sin", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), 0 }, +{"split", Op_builtin, LEX_BUILTIN, A(2)|A(3)|A(4), do_split }, +{"sprintf", Op_builtin, LEX_BUILTIN, 0, do_sprintf }, +{"sqrt", Op_builtin, LEX_BUILTIN, A(1), 0 }, +{"srand", Op_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), 0 }, #if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */ -{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|DEBUG_USE, stopme, 0}, +{"stopme", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|DEBUG_USE, stopme }, #endif -{"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime, 0}, -{"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), do_strtonum, MPF(strtonum)}, -{"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0, 0}, -{"substr", Op_builtin, LEX_BUILTIN, A(2)|A(3), do_substr, 0}, -{"switch", Op_K_switch, LEX_SWITCH, GAWKX|BREAK, 0, 0}, -{"system", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system, 0}, -{"systime", Op_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime, 0}, -{"tolower", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower, 0}, -{"toupper", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper, 0}, -{"while", Op_K_while, LEX_WHILE, BREAK|CONTINUE, 0, 0}, -{"xor", Op_builtin, LEX_BUILTIN, GAWKX, do_xor, MPF(xor)}, +{"strftime", Op_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2)|A(3), do_strftime }, +{"strtonum", Op_builtin, LEX_BUILTIN, GAWKX|A(1), 0 }, +{"sub", Op_sub_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), 0 }, +{"substr", Op_builtin, LEX_BUILTIN, A(2)|A(3), do_substr }, +{"switch", Op_K_switch, LEX_SWITCH, GAWKX|BREAK, 0 }, +{"system", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system }, +{"systime", Op_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime }, +{"tolower", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower }, +{"toupper", Op_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper }, +{"while", Op_K_while, LEX_WHILE, BREAK|CONTINUE, 0 }, +{"xor", Op_builtin, LEX_BUILTIN, GAWKX, 0 }, }; #if MBS_SUPPORT @@ -1944,53 +1937,12 @@ getfname(NODE *(*fptr)(int)) j = sizeof(tokentab) / sizeof(tokentab[0]); /* linear search, no other way to do it */ for (i = 0; i < j; i++) - if (tokentab[i].ptr == fptr || tokentab[i].ptr2 == fptr) + if (tokentab[i].ptr == fptr) return tokentab[i].operator; return NULL; } -/* negate_num --- negate a number in NODE */ - -void -negate_num(NODE *n) -{ -#ifdef HAVE_MPFR - int tval = 0; -#endif - - if (! is_mpg_number(n)) { - n->numbr = -n->numbr; - return; - } - -#ifdef HAVE_MPFR - if (is_mpg_integer(n)) { - if (! iszero(n)) { - mpz_neg(n->mpg_i, n->mpg_i); - return; - } - - /* - * 0 --> -0 conversion. Requires turning the MPG integer - * into an MPFR float. - */ - - mpz_clear(n->mpg_i); /* release the integer storage */ - - /* Convert and fall through. */ - tval = mpfr_set_d(n->mpg_numbr, 0.0, ROUND_MODE); - IEEE_FMT(n->mpg_numbr, tval); - n->flags &= ~MPZN; - n->flags |= MPFN; - } - - /* mpfr float case */ - tval = mpfr_neg(n->mpg_numbr, n->mpg_numbr, ROUND_MODE); - IEEE_FMT(n->mpg_numbr, tval); -#endif -} - /* print_included_from --- print `Included from ..' file names and locations */ static void @@ -2159,6 +2111,18 @@ yyerror(const char *m, ...) efree(buf); } +void +init_parser(const bltin_t *numbr_bltins) +{ + int i, tokentab_idx; + + for (i = 0; numbr_bltins[i].name != NULL; i++) { + tokentab_idx = check_special(numbr_bltins[i].name); + tokentab[tokentab_idx].ptr = numbr_bltins[i].ptr; + } +} + + /* mk_program --- create a single list of instructions */ static INSTRUCTION * @@ -2997,7 +2961,6 @@ yylex(void) char *tokkey; bool inhex = false; bool intlstr = false; - AWKNUM d; #define GET_INSTRUCTION(op) bcalloc(op, 1, sourceline) @@ -3575,32 +3538,7 @@ retry: } } -#ifdef HAVE_MPFR - if (do_mpfr) { - NODE *r; - - if (! seen_point && ! seen_e) { - r = mpg_integer(); - mpg_strtoui(r->mpg_i, tokstart, strlen(tokstart), NULL, base); - errno = 0; - } else { - int tval; - r = mpg_float(); - tval = mpfr_strtofr(r->mpg_numbr, tokstart, NULL, base, ROUND_MODE); - errno = 0; - IEEE_FMT(r->mpg_numbr, tval); - } - yylval->memory = r; - return lasttok = YNUMBER; - } -#endif - if (base != 10) - d = nondec2awknum(tokstart, strlen(tokstart)); - else - d = atof(tokstart); - yylval->memory = make_number(d); - if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) - yylval->memory->flags |= NUMINT; + yylval->memory = str2node(tokstart, NULL, base, ! (seen_point || seen_e)); return lasttok = YNUMBER; case '&': @@ -3904,13 +3842,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) } } -#ifdef HAVE_MPFR - /* N.B.: If necessary, add special processing for alternate builtin, below */ - if (do_mpfr && tokentab[idx].ptr2) - r->builtin = tokentab[idx].ptr2; - else -#endif - r->builtin = tokentab[idx].ptr; + r->builtin = tokentab[idx].ptr; /* special case processing for a few builtins */ @@ -3934,11 +3866,7 @@ snode(INSTRUCTION *subn, INSTRUCTION *r) arg = subn->nexti; if (arg->nexti == arg->lasti && arg->nexti->opcode == Op_push) arg->nexti->opcode = Op_push_arg; /* argument may be array */ - } else if (r->builtin == do_div -#ifdef HAVE_MPFR - || r->builtin == MPF(div) -#endif - ) { + } else if ((tokentab[idx].flags & ARG3_IS_ARR) != 0) { arg = subn->nexti->lasti->nexti->lasti->nexti; /* 3rd arg list */ ip = arg->lasti; if (ip->opcode == Op_push) @@ -4132,26 +4060,12 @@ valinfo(NODE *n, Func_print print_func, FILE *fp) pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false); print_func(fp, "\n"); } else if (n->flags & NUMBER) { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - print_func(fp, "%.17g\n", n->numbr); + print_func(fp, "%s\n", fmt_number("%.17g", n)); } else if (n->flags & STRCUR) { pp_string_fp(print_func, fp, n->stptr, n->stlen, '"', false); print_func(fp, "\n"); } else if (n->flags & NUMCUR) { -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - print_func(fp, "%s\n", mpg_fmt("%.17R*g", ROUND_MODE, n->mpg_numbr)); - else if (is_mpg_integer(n)) - print_func(fp, "%s\n", mpg_fmt("%Zd", n->mpg_i)); - else -#endif - print_func(fp, "%.17g\n", n->numbr); + print_func(fp, "%s\n", fmt_number("%.17g", n)); } else print_func(fp, "?? flags %s\n", flags2str(n->flags)); } @@ -4619,20 +4533,15 @@ isnoeffect(OPCODE type) { switch (type) { case Op_times: - case Op_times_i: case Op_quotient: - case Op_quotient_i: case Op_mod: - case Op_mod_i: case Op_plus: - case Op_plus_i: case Op_minus: - case Op_minus_i: case Op_subscript: case Op_concat: case Op_exp: - case Op_exp_i: case Op_unary_minus: + case Op_unary_plus: case Op_field_spec: case Op_and_final: case Op_or_final: @@ -4735,113 +4644,6 @@ dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2) fflush(stdout); } -/* mk_binary --- instructions for binary operators */ - -static INSTRUCTION * -mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op) -{ - INSTRUCTION *ip1,*ip2; - AWKNUM res; - - ip2 = s2->nexti; - if (s2->lasti == ip2 && ip2->opcode == Op_push_i) { - /* do any numeric constant folding */ - ip1 = s1->nexti; - if (do_optimize - && ip1 == s1->lasti && ip1->opcode == Op_push_i - && (ip1->memory->flags & (MPFN|MPZN|STRCUR|STRING)) == 0 - && (ip2->memory->flags & (MPFN|MPZN|STRCUR|STRING)) == 0 - ) { - NODE *n1 = ip1->memory, *n2 = ip2->memory; - res = force_number(n1)->numbr; - (void) force_number(n2); - switch (op->opcode) { - case Op_times: - res *= n2->numbr; - break; - case Op_quotient: - if (n2->numbr == 0.0) { - /* don't fatalize, allow parsing rest of the input */ - error_ln(op->source_line, _("division by zero attempted")); - goto regular; - } - - res /= n2->numbr; - break; - case Op_mod: - if (n2->numbr == 0.0) { - /* don't fatalize, allow parsing rest of the input */ - error_ln(op->source_line, _("division by zero attempted in `%%'")); - goto regular; - } -#ifdef HAVE_FMOD - res = fmod(res, n2->numbr); -#else /* ! HAVE_FMOD */ - (void) modf(res / n2->numbr, &res); - res = n1->numbr - res * n2->numbr; -#endif /* ! HAVE_FMOD */ - break; - case Op_plus: - res += n2->numbr; - break; - case Op_minus: - res -= n2->numbr; - break; - case Op_exp: - res = calc_exp(res, n2->numbr); - break; - default: - goto regular; - } - - op->opcode = Op_push_i; - op->memory = make_number(res); - unref(n1); - unref(n2); - bcfree(ip1); - bcfree(ip2); - bcfree(s1); - bcfree(s2); - return list_create(op); - } else { - /* do basic arithmetic optimisation */ - /* convert (Op_push_i Node_val) + (Op_plus) to (Op_plus_i Node_val) */ - switch (op->opcode) { - case Op_times: - op->opcode = Op_times_i; - break; - case Op_quotient: - op->opcode = Op_quotient_i; - break; - case Op_mod: - op->opcode = Op_mod_i; - break; - case Op_plus: - op->opcode = Op_plus_i; - break; - case Op_minus: - op->opcode = Op_minus_i; - break; - case Op_exp: - op->opcode = Op_exp_i; - break; - default: - goto regular; - } - - op->memory = ip2->memory; - bcfree(ip2); - bcfree(s2); /* Op_list */ - return list_append(s1, op); - } - } - -regular: - /* append lists s1, s2 and add `op' bytecode */ - (void) list_merge(s1, s2); - return list_append(s1, op); -} - /* mk_boolean --- instructions for boolean and, or */ static INSTRUCTION * @@ -5239,8 +5041,8 @@ optimize_assignment(INSTRUCTION *exp) i3->opcode = Op_store_sub; i3->memory = i2->memory; i3->expr_count = 1; /* sub_count shadows memory, - * so use expr_count instead. - */ + * so use expr_count instead + */ i3->nexti = NULL; i2->opcode = Op_no_op; bcfree(i1); /* Op_assign */ @@ -5723,10 +5525,6 @@ lookup_builtin(const char *name) if (mid == -1 || tokentab[mid].class != LEX_BUILTIN) return NULL; -#ifdef HAVE_MPFR - if (do_mpfr) - return tokentab[mid].ptr2; -#endif return tokentab[mid].ptr; } diff --git a/awklib/ChangeLog b/awklib/ChangeLog index 6ef0bbde..4173938f 100644 --- a/awklib/ChangeLog +++ b/awklib/ChangeLog @@ -20,6 +20,10 @@ * eg/lib/inplace.awk: Add new file generated from doc/gawk.texi. +2013-01-06 John Haque <j.eh@mchsi.com> + + * eg/lib/repl_math.awk: New file. + 2012-12-24 Arnold D. Robbins <arnold@skeeve.com> * 4.0.2: Release tar ball made. @@ -23,14 +23,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - #include "awk.h" +#include "format.h" + #if defined(HAVE_FCNTL_H) #include <fcntl.h> #endif -#include <math.h> -#include "random.h" -#include "floatmagic.h" #if defined(HAVE_POPEN_H) #include "popen.h" @@ -59,16 +57,6 @@ #define SIZE_MAX ((size_t) -1) #endif -#define DEFAULT_G_PRECISION 6 - -static size_t mbc_byte_count(const char *ptr, size_t numchars); -static size_t mbc_char_count(const char *ptr, size_t numbytes); - -/* Can declare these, since we always use the random shipped with gawk */ -extern char *initstate(unsigned long seed, char *state, long n); -extern char *setstate(char *state); -extern long random(void); -extern void srandom(unsigned long seed); extern NODE **args_array; extern int max_args; @@ -86,12 +74,6 @@ fatal(_("attempt to use array `%s' in a scalar context"), array_vname(s1)); \ }} while (false) -/* - * Since we supply the version of random(), we know what - * value to use here. - */ -#define GAWK_RANDOM_MAX 0x7fffffffL - /* efwrite --- like fwrite, but with error checking */ static void @@ -135,25 +117,6 @@ wrerror: errno ? strerror(errno) : _("reason unknown")); } -/* do_exp --- exponential function */ - -NODE * -do_exp(int nargs) -{ - NODE *tmp; - double d, res; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("exp: received non-numeric argument")); - d = force_number(tmp)->numbr; - DEREF(tmp); - errno = 0; - res = exp(d); - if (errno == ERANGE) - warning(_("exp: argument %g is out of range"), d); - return make_number((AWKNUM) res); -} /* stdfile --- return fp for a standard file */ @@ -463,35 +426,6 @@ out: return make_number((AWKNUM) ret); } -/* double_to_int --- convert double to int, used several places */ - -double -double_to_int(double d) -{ - if (d >= 0) - d = floor(d); - else - d = ceil(d); - return d; -} - -/* do_int --- convert double to int for awk */ - -NODE * -do_int(int nargs) -{ - NODE *tmp; - double d; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("int: received non-numeric argument")); - d = force_number(tmp)->numbr; - d = double_to_int(d); - DEREF(tmp); - return make_number((AWKNUM) d); -} - /* do_isarray --- check if argument is array */ NODE * @@ -562,1054 +496,6 @@ do_length(int nargs) return make_number((AWKNUM) len); } -/* do_log --- the log function */ - -NODE * -do_log(int nargs) -{ - NODE *tmp; - double d, arg; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("log: received non-numeric argument")); - arg = force_number(tmp)->numbr; - if (arg < 0.0) - warning(_("log: received negative argument %g"), arg); - d = log(arg); - DEREF(tmp); - return make_number((AWKNUM) d); -} - - -#ifdef HAVE_MPFR - -/* - * mpz2mpfr --- convert an arbitrary-precision integer to a float - * without any loss of precision. The returned value is only - * good for temporary use. - */ - - -static mpfr_ptr -mpz2mpfr(mpz_ptr zi) -{ - size_t prec; - static mpfr_t mpfrval; - static bool inited = false; - int tval; - - /* estimate minimum precision for exact conversion */ - prec = mpz_sizeinbase(zi, 2); /* most significant 1 bit position starting at 1 */ - prec -= (size_t) mpz_scan1(zi, 0); /* least significant 1 bit index starting at 0 */ - if (prec < MPFR_PREC_MIN) - prec = MPFR_PREC_MIN; - else if (prec > MPFR_PREC_MAX) - prec = MPFR_PREC_MAX; - - if (! inited) { - mpfr_init2(mpfrval, prec); - inited = true; - } else - mpfr_set_prec(mpfrval, prec); - tval = mpfr_set_z(mpfrval, zi, ROUND_MODE); - IEEE_FMT(mpfrval, tval); - return mpfrval; -} -#endif - -/* - * format_tree() formats arguments of sprintf, - * and accordingly to a fmt_string providing a format like in - * printf family from C library. Returns a string node which value - * is a formatted string. Called by sprintf function. - * - * It is one of the uglier parts of gawk. Thanks to Michal Jaegermann - * for taming this beast and making it compatible with ANSI C. - */ - -NODE * -format_tree( - const char *fmt_string, - size_t n0, - NODE **the_args, - long num_args) -{ -/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */ -/* difference of pointers should be of ptrdiff_t type, but let us be kind */ -#define bchunk(s, l) if (l) { \ - while ((l) > ofre) { \ - size_t olen = obufout - obuf; \ - erealloc(obuf, char *, osiz * 2, "format_tree"); \ - ofre += osiz; \ - osiz *= 2; \ - obufout = obuf + olen; \ - } \ - memcpy(obufout, s, (size_t) (l)); \ - obufout += (l); \ - ofre -= (l); \ -} - -/* copy one byte from 's' to 'obufout' checking for space in the process */ -#define bchunk_one(s) { \ - if (ofre < 1) { \ - size_t olen = obufout - obuf; \ - erealloc(obuf, char *, osiz * 2, "format_tree"); \ - ofre += osiz; \ - osiz *= 2; \ - obufout = obuf + olen; \ - } \ - *obufout++ = *s; \ - --ofre; \ -} - -/* Is there space for something L big in the buffer? */ -#define chksize(l) if ((l) >= ofre) { \ - size_t olen = obufout - obuf; \ - size_t delta = osiz+l-ofre; \ - erealloc(obuf, char *, osiz + delta, "format_tree"); \ - obufout = obuf + olen; \ - ofre += delta; \ - osiz += delta; \ -} - - size_t cur_arg = 0; - NODE *r = NULL; - int i, nc; - bool toofew = false; - char *obuf, *obufout; - size_t osiz, ofre; - const char *chbuf; - const char *s0, *s1; - int cs1; - NODE *arg; - long fw, prec, argnum; - bool used_dollar; - bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format; - long *cur = NULL; - uintmax_t uval; - bool sgn; - int base; - /* - * Although this is an array, the elements serve two different - * purposes. The first element is the general buffer meant - * to hold the entire result string. The second one is a - * temporary buffer for large floating point values. They - * could just as easily be separate variables, and the - * code might arguably be clearer. - */ - struct { - char *buf; - size_t bufsize; - char stackbuf[30]; - } cpbufs[2]; -#define cpbuf cpbufs[0].buf - char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)]; - char *cp; - const char *fill; - AWKNUM tmpval = 0.0; - char signchar = '\0'; - size_t len; - bool zero_flag = false; - bool quote_flag = false; - int ii, jj; - char *chp; - size_t copy_count, char_count; -#ifdef HAVE_MPFR - mpz_ptr zi; - mpfr_ptr mf; -#endif - enum { MP_NONE = 0, MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type; - - static const char sp[] = " "; - static const char zero_string[] = "0"; - static const char lchbuf[] = "0123456789abcdef"; - static const char Uchbuf[] = "0123456789ABCDEF"; - -#define INITIAL_OUT_SIZE 512 - emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree"); - obufout = obuf; - osiz = INITIAL_OUT_SIZE; - ofre = osiz - 2; - - cur_arg = 1; - - { - size_t k; - for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) { - cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf); - cpbufs[k].buf = cpbufs[k].stackbuf; - } - } - - /* - * The point of this goop is to grow the buffer - * holding the converted number, so that large - * values don't overflow a fixed length buffer. - */ -#define PREPEND(CH) do { \ - if (cp == cpbufs[0].buf) { \ - char *prev = cpbufs[0].buf; \ - emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \ - "format_tree"); \ - memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev, \ - cpbufs[0].bufsize); \ - cpbufs[0].bufsize *= 2; \ - if (prev != cpbufs[0].stackbuf) \ - efree(prev); \ - cend = cpbufs[0].buf+cpbufs[0].bufsize; \ - } \ - *--cp = (CH); \ -} while(0) - - /* - * Check first for use of `count$'. - * If plain argument retrieval was used earlier, choke. - * Otherwise, return the requested argument. - * If not `count$' now, but it was used earlier, choke. - * If this format is more than total number of args, choke. - * Otherwise, return the current argument. - */ -#define parse_next_arg() { \ - if (argnum > 0) { \ - if (cur_arg > 1) { \ - msg(_("fatal: must use `count$' on all formats or none")); \ - goto out; \ - } \ - arg = the_args[argnum]; \ - } else if (used_dollar) { \ - msg(_("fatal: must use `count$' on all formats or none")); \ - arg = 0; /* shutup the compiler */ \ - goto out; \ - } else if (cur_arg >= num_args) { \ - arg = 0; /* shutup the compiler */ \ - toofew = true; \ - break; \ - } else { \ - arg = the_args[cur_arg]; \ - cur_arg++; \ - } \ -} - - need_format = false; - used_dollar = false; - - s0 = s1 = fmt_string; - while (n0-- > 0) { - if (*s1 != '%') { - s1++; - continue; - } - need_format = true; - bchunk(s0, s1 - s0); - s0 = s1; - cur = &fw; - fw = 0; - prec = 0; - base = 0; - argnum = 0; - base = 0; - have_prec = false; - signchar = '\0'; - zero_flag = false; - quote_flag = false; -#ifdef HAVE_MPFR - mf = NULL; - zi = NULL; -#endif - fmt_type = MP_NONE; - - lj = alt = big_flag = bigbig_flag = small_flag = false; - fill = sp; - cp = cend; - chbuf = lchbuf; - s1++; - -retry: - if (n0-- == 0) /* ran out early! */ - break; - - switch (cs1 = *s1++) { - case (-1): /* dummy case to allow for checking */ -check_pos: - if (cur != &fw) - break; /* reject as a valid format */ - goto retry; - case '%': - need_format = false; - /* - * 29 Oct. 2002: - * The C99 standard pages 274 and 279 seem to imply that - * since there's no arg converted, the field width doesn't - * apply. The code already was that way, but this - * comment documents it, at least in the code. - */ - if (do_lint) { - const char *msg = NULL; - - if (fw && ! have_prec) - msg = _("field width is ignored for `%%' specifier"); - else if (fw == 0 && have_prec) - msg = _("precision is ignored for `%%' specifier"); - else if (fw && have_prec) - msg = _("field width and precision are ignored for `%%' specifier"); - - if (msg != NULL) - lintwarn("%s", msg); - } - bchunk_one("%"); - s0 = s1; - break; - - case '0': - /* - * Only turn on zero_flag if we haven't seen - * the field width or precision yet. Otherwise, - * screws up floating point formatting. - */ - if (cur == & fw) - zero_flag = true; - if (lj) - goto retry; - /* FALL through */ - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (cur == NULL) - break; - if (prec >= 0) - *cur = cs1 - '0'; - /* - * with a negative precision *cur is already set - * to -1, so it will remain negative, but we have - * to "eat" precision digits in any case - */ - while (n0 > 0 && *s1 >= '0' && *s1 <= '9') { - --n0; - *cur = *cur * 10 + *s1++ - '0'; - } - if (prec < 0) /* negative precision is discarded */ - have_prec = false; - if (cur == &prec) - cur = NULL; - if (n0 == 0) /* badly formatted control string */ - continue; - goto retry; - case '$': - if (do_traditional) { - msg(_("fatal: `$' is not permitted in awk formats")); - goto out; - } - - if (cur == &fw) { - argnum = fw; - fw = 0; - used_dollar = true; - if (argnum <= 0) { - msg(_("fatal: arg count with `$' must be > 0")); - goto out; - } - if (argnum >= num_args) { - msg(_("fatal: arg count %ld greater than total number of supplied arguments"), argnum); - goto out; - } - } else { - msg(_("fatal: `$' not permitted after period in format")); - goto out; - } - - goto retry; - case '*': - if (cur == NULL) - break; - if (! do_traditional && isdigit((unsigned char) *s1)) { - int val = 0; - - for (; n0 > 0 && *s1 && isdigit((unsigned char) *s1); s1++, n0--) { - val *= 10; - val += *s1 - '0'; - } - if (*s1 != '$') { - msg(_("fatal: no `$' supplied for positional field width or precision")); - goto out; - } else { - s1++; - n0--; - } - if (val >= num_args) { - toofew = true; - break; - } - arg = the_args[val]; - } else { - parse_next_arg(); - } - (void) force_number(arg); - *cur = get_number_si(arg); - if (*cur < 0 && cur == &fw) { - *cur = -*cur; - lj++; - } - if (cur == &prec) { - if (*cur >= 0) - have_prec = true; - else - have_prec = false; - cur = NULL; - } - goto retry; - case ' ': /* print ' ' or '-' */ - /* 'space' flag is ignored */ - /* if '+' already present */ - if (signchar != false) - goto check_pos; - /* FALL THROUGH */ - case '+': /* print '+' or '-' */ - signchar = cs1; - goto check_pos; - case '-': - if (prec < 0) - break; - if (cur == &prec) { - prec = -1; - goto retry; - } - fill = sp; /* if left justified then other */ - lj++; /* filling is ignored */ - goto check_pos; - case '.': - if (cur != &fw) - break; - cur = ≺ - have_prec = true; - goto retry; - case '#': - alt = true; - goto check_pos; - case '\'': -#if defined(HAVE_LOCALE_H) - quote_flag = true; - goto check_pos; -#else - goto retry; -#endif - case 'l': - if (big_flag) - break; - else { - static bool warned = false; - - if (do_lint && ! warned) { - lintwarn(_("`l' is meaningless in awk formats; ignored")); - warned = true; - } - if (do_posix) { - msg(_("fatal: `l' is not permitted in POSIX awk formats")); - goto out; - } - } - big_flag = true; - goto retry; - case 'L': - if (bigbig_flag) - break; - else { - static bool warned = false; - - if (do_lint && ! warned) { - lintwarn(_("`L' is meaningless in awk formats; ignored")); - warned = true; - } - if (do_posix) { - msg(_("fatal: `L' is not permitted in POSIX awk formats")); - goto out; - } - } - bigbig_flag = true; - goto retry; - case 'h': - if (small_flag) - break; - else { - static bool warned = false; - - if (do_lint && ! warned) { - lintwarn(_("`h' is meaningless in awk formats; ignored")); - warned = true; - } - if (do_posix) { - msg(_("fatal: `h' is not permitted in POSIX awk formats")); - goto out; - } - } - small_flag = true; - goto retry; - case 'c': - need_format = false; - parse_next_arg(); - /* user input that looks numeric is numeric */ - if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM) - (void) force_number(arg); - if ((arg->flags & NUMBER) != 0) { - uval = get_number_uj(arg); -#if MBS_SUPPORT - if (gawk_mb_cur_max > 1) { - char buf[100]; - wchar_t wc; - mbstate_t mbs; - size_t count; - - memset(& mbs, 0, sizeof(mbs)); - - /* handle systems with too small wchar_t */ - if (sizeof(wchar_t) < 4 && uval > 0xffff) { - if (do_lint) - lintwarn( - _("[s]printf: value %g is too big for %%c format"), - arg->numbr); - - goto out0; - } - - wc = uval; - - count = wcrtomb(buf, wc, & mbs); - if (count == 0 - || count == (size_t) -1) { - if (do_lint) - lintwarn( - _("[s]printf: value %g is not a valid wide character"), - arg->numbr); - - goto out0; - } - - memcpy(cpbuf, buf, count); - prec = count; - cp = cpbuf; - goto pr_tail; - } -out0: - ; - /* else, - fall through */ -#endif - cpbuf[0] = uval; - prec = 1; - cp = cpbuf; - goto pr_tail; - } - /* - * As per POSIX, only output first character of a - * string value. Thus, we ignore any provided - * precision, forcing it to 1. (Didn't this - * used to work? 6/2003.) - */ - cp = arg->stptr; - prec = 1; -#if MBS_SUPPORT - /* - * First character can be multiple bytes if - * it's a multibyte character. Grr. - */ - if (gawk_mb_cur_max > 1) { - mbstate_t state; - size_t count; - - memset(& state, 0, sizeof(state)); - count = mbrlen(cp, arg->stlen, & state); - if (count != (size_t) -1 && count != (size_t) -2 && count > 0) { - prec = count; - /* may need to increase fw so that padding happens, see pr_tail code */ - if (fw > 0) - fw += count - 1; - } - } -#endif - goto pr_tail; - case 's': - need_format = false; - parse_next_arg(); - arg = force_string(arg); - if (fw == 0 && ! have_prec) - prec = arg->stlen; - else { - char_count = mbc_char_count(arg->stptr, arg->stlen); - if (! have_prec || prec > char_count) - prec = char_count; - } - cp = arg->stptr; - goto pr_tail; - case 'd': - case 'i': - need_format = false; - parse_next_arg(); - (void) force_number(arg); -#ifdef HAVE_MPFR - if (is_mpg_float(arg)) - goto mpf0; - else if (is_mpg_integer(arg)) - goto mpz0; - else -#endif - tmpval = arg->numbr; - - /* - * Check for Nan or Inf. - */ - if (isnan(tmpval) || isinf(tmpval)) - goto out_of_range; - else - tmpval = double_to_int(tmpval); - - /* - * ``The result of converting a zero value with a - * precision of zero is no characters.'' - */ - if (have_prec && prec == 0 && tmpval == 0) - goto pr_tail; - - if (tmpval < 0) { - tmpval = -tmpval; - sgn = true; - } else { - if (tmpval == -0.0) - /* avoid printing -0 */ - tmpval = 0.0; - sgn = false; - } - /* - * Use snprintf return value to tell if there - * is enough room in the buffer or not. - */ - while ((i = snprintf(cpbufs[1].buf, - cpbufs[1].bufsize, "%.0f", - tmpval)) >= - cpbufs[1].bufsize) { - if (cpbufs[1].buf == cpbufs[1].stackbuf) - cpbufs[1].buf = NULL; - if (i > 0) { - cpbufs[1].bufsize += ((i > cpbufs[1].bufsize) ? - i : cpbufs[1].bufsize); - } - else - cpbufs[1].bufsize *= 2; - assert(cpbufs[1].bufsize > 0); - erealloc(cpbufs[1].buf, char *, - cpbufs[1].bufsize, "format_tree"); - } - if (i < 1) - goto out_of_range; -#if defined(HAVE_LOCALE_H) - quote_flag = (quote_flag && loc.thousands_sep[0] != 0); -#endif - chp = &cpbufs[1].buf[i-1]; - ii = jj = 0; - do { - PREPEND(*chp); - chp--; i--; -#if defined(HAVE_LOCALE_H) - if (quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (i) { /* only add if more digits coming */ - int k; - const char *ts = loc.thousands_sep; - - for (k = strlen(ts) - 1; k >= 0; k--) { - PREPEND(ts[k]); - } - } - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = false; - else { - ii++; - jj = 0; - } - } -#endif - } while (i > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (sgn) - PREPEND('-'); - else if (signchar) - PREPEND(signchar); - /* - * When to fill with zeroes is of course not simple. - * First: No zero fill if left-justifying. - * Next: There seem to be two cases: - * A '0' without a precision, e.g. %06d - * A precision with no field width, e.g. %.10d - * Any other case, we don't want to fill with zeroes. - */ - if (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))) - fill = zero_string; - if (prec > fw) - fw = prec; - prec = cend - cp; - if (fw > prec && ! lj && fill != sp - && (*cp == '-' || signchar)) { - bchunk_one(cp); - cp++; - prec--; - fw--; - } - goto pr_tail; - case 'X': - chbuf = Uchbuf; /* FALL THROUGH */ - case 'x': - base += 6; /* FALL THROUGH */ - case 'u': - base += 2; /* FALL THROUGH */ - case 'o': - base += 8; - need_format = false; - parse_next_arg(); - (void) force_number(arg); -#ifdef HAVE_MPFR - if (is_mpg_integer(arg)) { -mpz0: - zi = arg->mpg_i; - - if (cs1 != 'd' && cs1 != 'i') { - if (mpz_sgn(zi) <= 0) { - /* - * Negative value or 0 requires special handling. - * Unlike MPFR, GMP does not allow conversion - * to (u)intmax_t. So we first convert GMP type to - * a MPFR type. - */ - mf = mpz2mpfr(zi); - goto mpf1; - } - signchar = '\0'; /* Don't print '+' */ - } - - /* See comments above about when to fill with zeros */ - zero_flag = (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))); - - fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; - goto fmt0; - - } else if (is_mpg_float(arg)) { -mpf0: - mf = arg->mpg_numbr; - if (! mpfr_number_p(mf)) { - /* inf or NaN */ - cs1 = 'g'; - fmt_type = MP_FLOAT; - goto fmt1; - } - - if (cs1 != 'd' && cs1 != 'i') { -mpf1: - /* - * The output of printf("%#.0x", 0) is 0 instead of 0x, hence <= in - * the comparison below. - */ - if (mpfr_sgn(mf) <= 0) { - if (! mpfr_fits_intmax_p(mf, ROUND_MODE)) { - /* -ve number is too large */ - cs1 = 'g'; - fmt_type = MP_FLOAT; - goto fmt1; - } - - tmpval = uval = (uintmax_t) mpfr_get_sj(mf, ROUND_MODE); - if (! alt && have_prec && prec == 0 && tmpval == 0) - goto pr_tail; /* printf("%.0x", 0) is no characters */ - goto int0; - } - signchar = '\0'; /* Don't print '+' */ - } - - /* See comments above about when to fill with zeros */ - zero_flag = (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))); - - (void) mpfr_get_z(mpzval, mf, MPFR_RNDZ); /* convert to GMP integer */ - fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; - zi = mpzval; - goto fmt0; - } else -#endif - tmpval = arg->numbr; - - /* - * ``The result of converting a zero value with a - * precision of zero is no characters.'' - * - * If I remember the ANSI C standard, though, - * it says that for octal conversions - * the precision is artificially increased - * to add an extra 0 if # is supplied. - * Indeed, in C, - * printf("%#.0o\n", 0); - * prints a single 0. - */ - if (! alt && have_prec && prec == 0 && tmpval == 0) - goto pr_tail; - - if (tmpval < 0) { - uval = (uintmax_t) (intmax_t) tmpval; - if ((AWKNUM)(intmax_t)uval != double_to_int(tmpval)) - goto out_of_range; - } else { - uval = (uintmax_t) tmpval; - if ((AWKNUM)uval != double_to_int(tmpval)) - goto out_of_range; - } -#ifdef HAVE_MPFR - int0: -#endif -#if defined(HAVE_LOCALE_H) - quote_flag = (quote_flag && loc.thousands_sep[0] != 0); -#endif - /* - * When to fill with zeroes is of course not simple. - * First: No zero fill if left-justifying. - * Next: There seem to be two cases: - * A '0' without a precision, e.g. %06d - * A precision with no field width, e.g. %.10d - * Any other case, we don't want to fill with zeroes. - */ - if (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))) - fill = zero_string; - ii = jj = 0; - do { - PREPEND(chbuf[uval % base]); - uval /= base; -#if defined(HAVE_LOCALE_H) - if (base == 10 && quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (uval) { /* only add if more digits coming */ - int k; - const char *ts = loc.thousands_sep; - - for (k = strlen(ts) - 1; k >= 0; k--) { - PREPEND(ts[k]); - } - } - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = false; - else { - ii++; - jj = 0; - } - } -#endif - } while (uval > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (alt && tmpval != 0) { - if (base == 16) { - PREPEND(cs1); - PREPEND('0'); - if (fill != sp) { - bchunk(cp, 2); - cp += 2; - fw -= 2; - } - } else if (base == 8) - PREPEND('0'); - } - base = 0; - if (prec > fw) - fw = prec; - prec = cend - cp; - pr_tail: - if (! lj) { - while (fw > prec) { - bchunk_one(fill); - fw--; - } - } - copy_count = prec; - if (fw == 0 && ! have_prec) - ; - else if (gawk_mb_cur_max > 1) { - if (cs1 == 's') { - assert(cp == arg->stptr || cp == cpbuf); - copy_count = mbc_byte_count(arg->stptr, prec); - } - /* prec was set by code for %c */ - /* else - copy_count = prec; */ - } - bchunk(cp, copy_count); - while (fw > prec) { - bchunk_one(fill); - fw--; - } - s0 = s1; - break; - - out_of_range: - /* out of range - emergency use of %g format */ - if (do_lint) - lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), - (double) tmpval, cs1); - cs1 = 'g'; - goto fmt1; - - case 'F': -#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 - cs1 = 'f'; - /* FALL THROUGH */ -#endif - case 'g': - case 'G': - case 'e': - case 'f': - case 'E': - need_format = false; - parse_next_arg(); - (void) force_number(arg); - - if (! is_mpg_number(arg)) - tmpval = arg->numbr; -#ifdef HAVE_MPFR - else if (is_mpg_float(arg)) { - mf = arg->mpg_numbr; - fmt_type = MP_FLOAT; - } else { - /* arbitrary-precision integer, convert to MPFR float */ - assert(mf == NULL); - mf = mpz2mpfr(arg->mpg_i); - fmt_type = MP_FLOAT; - } -#endif - fmt1: - if (! have_prec) - prec = DEFAULT_G_PRECISION; -#ifdef HAVE_MPFR - fmt0: -#endif - chksize(fw + prec + 11); /* 11 == slop */ - cp = cpbuf; - *cp++ = '%'; - if (lj) - *cp++ = '-'; - if (signchar) - *cp++ = signchar; - if (alt) - *cp++ = '#'; - if (zero_flag) - *cp++ = '0'; - if (quote_flag) - *cp++ = '\''; - -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, ""); -#endif - - switch (fmt_type) { -#ifdef HAVE_MPFR - case MP_INT_WITH_PREC: - sprintf(cp, "*.*Z%c", cs1); - while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, zi)) >= ofre) - chksize(nc) - break; - case MP_INT_WITHOUT_PREC: - sprintf(cp, "*Z%c", cs1); - while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, - (int) fw, zi)) >= ofre) - chksize(nc) - break; - case MP_FLOAT: - sprintf(cp, "*.*R*%c", cs1); - while ((nc = mpfr_snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, ROUND_MODE, mf)) >= ofre) - chksize(nc) - break; -#endif - default: - sprintf(cp, "*.*%c", cs1); - while ((nc = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(nc) - } - -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, "C"); -#endif - - len = strlen(obufout); - ofre -= len; - obufout += len; - s0 = s1; - break; - default: - if (do_lint && is_alpha(cs1)) - lintwarn(_("ignoring unknown format specifier character `%c': no argument converted"), cs1); - break; - } - if (toofew) { - msg("%s\n\t`%s'\n\t%*s%s", - _("fatal: not enough arguments to satisfy format string"), - fmt_string, (int) (s1 - fmt_string - 1), "", - _("^ ran out for this one")); - goto out; - } - } - if (do_lint) { - if (need_format) - lintwarn( - _("[s]printf: format specifier does not have control letter")); - if (cur_arg < num_args) - lintwarn( - _("too many arguments supplied for format string")); - } - bchunk(s0, s1 - s0); - r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED); - obuf = NULL; -out: - { - size_t k; - size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]); - for (k = 0; k < count; k++) { - if (cpbufs[k].buf != cpbufs[k].stackbuf) - efree(cpbufs[k].buf); - } - if (obuf != NULL) - efree(obuf); - } - - if (r == NULL) - gawk_exit(EXIT_FATAL); - return r; -} - - /* printf_common --- common code for sprintf and printf */ static NODE * @@ -1710,45 +596,25 @@ do_printf(int nargs, int redirtype) gawk_exit(EXIT_FATAL); } -/* do_sqrt --- do the sqrt function */ - -NODE * -do_sqrt(int nargs) -{ - NODE *tmp; - double arg; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("sqrt: received non-numeric argument")); - arg = (double) force_number(tmp)->numbr; - DEREF(tmp); - if (arg < 0.0) - warning(_("sqrt: called with negative argument %g"), arg); - return make_number((AWKNUM) sqrt(arg)); -} - /* do_substr --- do the substr function */ NODE * do_substr(int nargs) { - NODE *t1; - NODE *r; + NODE *t1, *t2, *t3 = NULL; + NODE *r = NULL; size_t indx; size_t length = 0; double d_index = 0, d_length = 0; size_t src_len; if (nargs == 3) { - t1 = POP_NUMBER(); - d_length = get_number_d(t1); - DEREF(t1); + t3 = POP_NUMBER(); + d_length = get_number_d(t3); } - t1 = POP_NUMBER(); - d_index = get_number_d(t1); - DEREF(t1); + t2 = POP_NUMBER(); + d_index = get_number_d(t2); t1 = POP_STRING(); @@ -1758,7 +624,6 @@ do_substr(int nargs) lintwarn(_("substr: length %g is not >= 1"), d_length); else if (do_lint == DO_LINT_INVALID && ! (d_length >= 0)) lintwarn(_("substr: length %g is not >= 0"), d_length); - DEREF(t1); /* * Return explicit null string instead of doing * dupnode(Nnull_string) so that if the result @@ -1766,10 +631,11 @@ do_substr(int nargs) * and lint, no error is reported about using * an uninitialized value. Same thing later, too. */ - return make_string("", 0); + r = make_string("", 0); + goto finish; } if (do_lint) { - if (double_to_int(d_length) != d_length) + if (! isinteger(t3)) lintwarn( _("substr: non-integer length %g will be truncated"), d_length); @@ -1792,7 +658,7 @@ do_substr(int nargs) d_index); d_index = 1; } - if (do_lint && double_to_int(d_index) != d_index) + if (do_lint && ! isinteger(t2)) lintwarn(_("substr: non-integer start index %g will be truncated"), d_index); @@ -1819,8 +685,8 @@ do_substr(int nargs) /* substr("", 1, 0) produces a warning only if LINT_ALL */ if (do_lint && (do_lint == DO_LINT_ALL || ((indx | length) != 0))) lintwarn(_("substr: source string is zero length")); - DEREF(t1); - return make_string("", 0); + r = make_string("", 0); + goto finish; } /* get total len of input string, for following checks */ @@ -1836,8 +702,8 @@ do_substr(int nargs) if (do_lint) lintwarn(_("substr: start index %g is past end of string"), d_index); - DEREF(t1); - return make_string("", 0); + r = make_string("", 0); + goto finish; } if (length > src_len - indx) { if (do_lint) @@ -1881,6 +747,10 @@ do_substr(int nargs) r = make_string(t1->stptr + indx, length); #endif +finish: + if (t3 != NULL) + DEREF(t3); + DEREF(t2); DEREF(t1); return r; } @@ -2346,175 +1216,6 @@ do_toupper(int nargs) return t2; } -/* do_atan2 --- do the atan2 function */ - -NODE * -do_atan2(int nargs) -{ - NODE *t1, *t2; - double d1, d2; - - POP_TWO_SCALARS(t1, t2); - if (do_lint) { - if ((t1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("atan2: received non-numeric first argument")); - if ((t2->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("atan2: received non-numeric second argument")); - } - d1 = force_number(t1)->numbr; - d2 = force_number(t2)->numbr; - DEREF(t1); - DEREF(t2); - return make_number((AWKNUM) atan2(d1, d2)); -} - -/* do_sin --- do the sin function */ - -NODE * -do_sin(int nargs) -{ - NODE *tmp; - double d; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("sin: received non-numeric argument")); - d = sin((double) force_number(tmp)->numbr); - DEREF(tmp); - return make_number((AWKNUM) d); -} - -/* do_cos --- do the cos function */ - -NODE * -do_cos(int nargs) -{ - NODE *tmp; - double d; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("cos: received non-numeric argument")); - d = cos((double) force_number(tmp)->numbr); - DEREF(tmp); - return make_number((AWKNUM) d); -} - -/* do_rand --- do the rand function */ - -static bool firstrand = true; -/* Some systems require this array to be integer aligned. Sigh. */ -#define SIZEOF_STATE 256 -static uint32_t istate[SIZEOF_STATE/sizeof(uint32_t)]; -static char *const state = (char *const) istate; - -/* ARGSUSED */ -NODE * -do_rand(int nargs ATTRIBUTE_UNUSED) -{ - double tmprand; -#define RAND_DIVISOR ((double)GAWK_RANDOM_MAX+1.0) - if (firstrand) { - (void) initstate((unsigned) 1, state, SIZEOF_STATE); - /* don't need to srandom(1), initstate() does it for us. */ - firstrand = false; - setstate(state); - } - /* - * Per historical practice and POSIX, return value N is - * - * 0 <= n < 1 - */ - /* - * Date: Wed, 28 Aug 2013 17:52:46 -0700 - * From: Bob Jewett <jewett@bill.scs.agilent.com> - * - * Call random() twice to fill in more bits in the value - * of the double. Also, there is a bug in random() such - * that when the values of successive values are combined - * like (rand1*rand2)^2, (rand3*rand4)^2, ... the - * resulting time series is not white noise. The - * following also seems to fix that bug. - * - * The add/subtract 0.5 keeps small bits from filling - * below 2^-53 in the double, not that anyone should be - * looking down there. - * - * Date: Wed, 25 Sep 2013 10:45:38 -0600 (MDT) - * From: "Nelson H. F. Beebe" <beebe@math.utah.edu> - * (4) The code is typical of many published fragments for converting - * from integer to floating-point, and I discuss the serious pitfalls - * in my book, because it leads to platform-dependent behavior at the - * end points of the interval [0,1] - * - * (5) the documentation in the gawk info node says - * - * `rand()' - * Return a random number. The values of `rand()' are uniformly - * distributed between zero and one. The value could be zero but is - * never one.(1) - * - * The division by RAND_DIVISOR may not guarantee that 1.0 is never - * returned: the programmer forgot the platform-dependent issue of - * rounding. - * - * For points 4 and 5, the safe way is a loop: - * - * double - * rand(void) // return value in [0.0, 1.0) - * { - * value = internal_rand(); - * - * while (value == 1.0) - * value = internal_rand(); - * - * return (value); - * } - */ - - do { - long d1, d2; - /* - * Do the calls in predictable order to avoid - * compiler differences in order of evaluation. - */ - d1 = random(); - d2 = random(); - tmprand = 0.5 + ( (d1/RAND_DIVISOR + d2) / RAND_DIVISOR ); - tmprand -= 0.5; - } while (tmprand == 1.0); - - return make_number((AWKNUM) tmprand); -} - -/* do_srand --- seed the random number generator */ - -NODE * -do_srand(int nargs) -{ - NODE *tmp; - static long save_seed = 1; - long ret = save_seed; /* SVR4 awk srand returns previous seed */ - - if (firstrand) { - (void) initstate((unsigned) 1, state, SIZEOF_STATE); - /* don't need to srandom(1), we're changing the seed below */ - firstrand = false; - (void) setstate(state); - } - - if (nargs == 0) - srandom((unsigned int) (save_seed = (long) time((time_t *) 0))); - else { - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("srand: received non-numeric argument")); - srandom((unsigned int) (save_seed = (long) force_number(tmp)->numbr)); - DEREF(tmp); - } - return make_number((AWKNUM) ret); -} - /* do_match --- match a regexp, set RSTART and RLENGTH, * optional third arg is array filled with text of * subpatterns enclosed in parens and start and len info. @@ -3094,316 +1795,6 @@ done: } -/* make_integer - Convert an integer to a number node. */ - -static NODE * -make_integer(uintmax_t n) -{ - n = adjust_uint(n); - - return make_number((AWKNUM) n); -} - -/* do_lshift --- perform a << operation */ - -NODE * -do_lshift(int nargs) -{ - NODE *s1, *s2; - uintmax_t uval, ushift, res; - AWKNUM val, shift; - - POP_TWO_SCALARS(s1, s2); - if (do_lint) { - if ((s1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("lshift: received non-numeric first argument")); - if ((s2->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("lshift: received non-numeric second argument")); - } - val = force_number(s1)->numbr; - shift = force_number(s2)->numbr; - if (do_lint) { - if (val < 0 || shift < 0) - lintwarn(_("lshift(%f, %f): negative values will give strange results"), val, shift); - if (double_to_int(val) != val || double_to_int(shift) != shift) - lintwarn(_("lshift(%f, %f): fractional values will be truncated"), val, shift); - if (shift >= sizeof(uintmax_t) * CHAR_BIT) - lintwarn(_("lshift(%f, %f): too large shift value will give strange results"), val, shift); - } - - DEREF(s1); - DEREF(s2); - - uval = (uintmax_t) val; - ushift = (uintmax_t) shift; - - res = uval << ushift; - return make_integer(res); -} - -/* do_rshift --- perform a >> operation */ - -NODE * -do_rshift(int nargs) -{ - NODE *s1, *s2; - uintmax_t uval, ushift, res; - AWKNUM val, shift; - - POP_TWO_SCALARS(s1, s2); - if (do_lint) { - if ((s1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("rshift: received non-numeric first argument")); - if ((s2->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("rshift: received non-numeric second argument")); - } - val = force_number(s1)->numbr; - shift = force_number(s2)->numbr; - if (do_lint) { - if (val < 0 || shift < 0) - lintwarn(_("rshift(%f, %f): negative values will give strange results"), val, shift); - if (double_to_int(val) != val || double_to_int(shift) != shift) - lintwarn(_("rshift(%f, %f): fractional values will be truncated"), val, shift); - if (shift >= sizeof(uintmax_t) * CHAR_BIT) - lintwarn(_("rshift(%f, %f): too large shift value will give strange results"), val, shift); - } - - DEREF(s1); - DEREF(s2); - - uval = (uintmax_t) val; - ushift = (uintmax_t) shift; - - res = uval >> ushift; - return make_integer(res); -} - -/* do_and --- perform an & operation */ - -NODE * -do_and(int nargs) -{ - NODE *s1; - uintmax_t res, uval; - AWKNUM val; - int i; - - res = ~0; /* start off with all ones */ - if (nargs < 2) - fatal(_("and: called with less than two arguments")); - - for (i = 1; nargs > 0; nargs--, i++) { - s1 = POP_SCALAR(); - if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("and: argument %d is non-numeric"), i); - - val = force_number(s1)->numbr; - if (do_lint && val < 0) - lintwarn(_("and: argument %d negative value %g will give strange results"), i, val); - - uval = (uintmax_t) val; - res &= uval; - - DEREF(s1); - } - - return make_integer(res); -} - -/* do_or --- perform an | operation */ - -NODE * -do_or(int nargs) -{ - NODE *s1; - uintmax_t res, uval; - AWKNUM val; - int i; - - res = 0; - if (nargs < 2) - fatal(_("or: called with less than two arguments")); - - for (i = 1; nargs > 0; nargs--, i++) { - s1 = POP_SCALAR(); - if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("or: argument %d is non-numeric"), i); - - val = force_number(s1)->numbr; - if (do_lint && val < 0) - lintwarn(_("or: argument %d negative value %g will give strange results"), i, val); - - uval = (uintmax_t) val; - res |= uval; - - DEREF(s1); - } - - return make_integer(res); -} - -/* do_xor --- perform an ^ operation */ - -NODE * -do_xor(int nargs) -{ - NODE *s1; - uintmax_t res, uval; - AWKNUM val; - int i; - - if (nargs < 2) - fatal(_("xor: called with less than two arguments")); - - res = 0; /* silence compiler warning */ - for (i = 1; nargs > 0; nargs--, i++) { - s1 = POP_SCALAR(); - if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("xor: argument %d is non-numeric"), i); - - val = force_number(s1)->numbr; - if (do_lint && val < 0) - lintwarn(_("xor: argument %d negative value %g will give strange results"), i, val); - - uval = (uintmax_t) val; - if (i == 1) - res = uval; - else - res ^= uval; - - DEREF(s1); - } - - return make_integer(res); -} - -/* do_compl --- perform a ~ operation */ - -NODE * -do_compl(int nargs) -{ - NODE *tmp; - double d; - uintmax_t uval; - - tmp = POP_SCALAR(); - if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("compl: received non-numeric argument")); - d = force_number(tmp)->numbr; - DEREF(tmp); - - if (do_lint) { - if (d < 0) - lintwarn(_("compl(%f): negative value will give strange results"), d); - if (double_to_int(d) != d) - lintwarn(_("compl(%f): fractional value will be truncated"), d); - } - - uval = (uintmax_t) d; - uval = ~ uval; - return make_integer(uval); -} - -/* do_strtonum --- the strtonum function */ - -NODE * -do_strtonum(int nargs) -{ - NODE *tmp; - AWKNUM d; - - tmp = POP_SCALAR(); - if ((tmp->flags & (NUMBER|NUMCUR)) != 0) - d = (AWKNUM) force_number(tmp)->numbr; - else if (get_numbase(tmp->stptr, use_lc_numeric) != 10) - d = nondec2awknum(tmp->stptr, tmp->stlen); - else - d = (AWKNUM) force_number(tmp)->numbr; - - DEREF(tmp); - return make_number((AWKNUM) d); -} - -/* nondec2awknum --- convert octal or hex value to double */ - -/* - * Because of awk's concatenation rules and the way awk.y:yylex() - * collects a number, this routine has to be willing to stop on the - * first invalid character. - */ - -AWKNUM -nondec2awknum(char *str, size_t len) -{ - AWKNUM retval = 0.0; - char save; - short val; - char *start = str; - - if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) { - /* - * User called strtonum("0x") or some such, - * so just quit early. - */ - if (len <= 2) - return (AWKNUM) 0.0; - - for (str += 2, len -= 2; len > 0; len--, str++) { - switch (*str) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - val = *str - '0'; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - val = *str - 'a' + 10; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - val = *str - 'A' + 10; - break; - default: - goto done; - } - retval = (retval * 16) + val; - } - } else if (*str == '0') { - for (; len > 0; len--) { - if (! isdigit((unsigned char) *str)) - goto done; - else if (*str == '8' || *str == '9') { - str = start; - goto decimal; - } - retval = (retval * 8) + (*str - '0'); - str++; - } - } else { -decimal: - save = str[len]; - retval = strtod(str, NULL); - str[len] = save; - } -done: - return retval; -} - /* do_dcgettext, do_dcngettext --- handle i18n translations */ #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT @@ -3537,7 +1928,6 @@ do_dcngettext(int nargs) NODE *tmp, *t1, *t2, *t3; char *string1, *string2; unsigned long number; - AWKNUM d; char *the_result; #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT @@ -3569,10 +1959,9 @@ do_dcngettext(int nargs) #endif t2 = POP_NUMBER(); /* third argument */ - d = get_number_d(t2); + number = get_number_ui(t2); DEREF(t2); - number = (unsigned long) double_to_int(d); t2 = POP_STRING(); /* second argument */ string2 = t2->stptr; t1 = POP_STRING(); /* first argument */ @@ -3632,134 +2021,3 @@ do_bindtextdomain(int nargs) return make_string(the_result, strlen(the_result)); } - -/* do_div --- do integer division, return quotient and remainder in dest array */ - -/* - * We define the semantics as: - * numerator = int(numerator) - * denominator = int(denonmator) - * quotient = int(numerator / denomator) - * remainder = int(numerator % denomator) - */ - -NODE * -do_div(int nargs) -{ - NODE *numerator, *denominator, *result; - double num, denom, quotient, remainder; - NODE *sub, **lhs; - - result = POP_PARAM(); - if (result->type != Node_var_array) - fatal(_("div: third argument is not an array")); - assoc_clear(result); - - denominator = POP_SCALAR(); - numerator = POP_SCALAR(); - - if (do_lint) { - if ((numerator->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("div: received non-numeric first argument")); - if ((denominator->flags & (NUMCUR|NUMBER)) == 0) - lintwarn(_("div: received non-numeric second argument")); - } - - (void) force_number(numerator); - (void) force_number(denominator); - num = double_to_int(get_number_d(numerator)); - denom = double_to_int(get_number_d(denominator)); - - if (denom == 0.0) - fatal(_("div: division by zero attempted")); - - quotient = double_to_int(num / denom); - /* - * FIXME: This code is duplicated, factor it out to a - * separate function. - */ -#ifdef HAVE_FMOD - remainder = fmod(num, denom); -#else /* ! HAVE_FMOD */ - (void) modf(num / denom, & remainder); - remainder = num - remainder * denom; -#endif /* ! HAVE_FMOD */ - remainder = double_to_int(remainder); - - sub = make_string("quotient", 8); - lhs = assoc_lookup(result, sub); - unref(*lhs); - *lhs = make_number((AWKNUM) quotient); - - sub = make_string("remainder", 9); - lhs = assoc_lookup(result, sub); - unref(*lhs); - *lhs = make_number((AWKNUM) remainder); - - return make_number((AWKNUM) 0.0); -} - - -/* mbc_byte_count --- return number of bytes for corresponding numchars multibyte characters */ - -static size_t -mbc_byte_count(const char *ptr, size_t numchars) -{ -#if MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - memset(& cur_state, 0, sizeof(cur_state)); - - assert(gawk_mb_cur_max > 1); - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numchars; /* no valid m.b. char */ - - for (; numchars > 0; numchars--) { - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum += mb_len; - ptr += mb_len; - } - - return sum; -#else - return numchars; -#endif -} - -/* mbc_char_count --- return number of m.b. chars in string, up to numbytes bytes */ - -static size_t -mbc_char_count(const char *ptr, size_t numbytes) -{ -#if MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - if (gawk_mb_cur_max == 1) - return numbytes; - - memset(& cur_state, 0, sizeof(cur_state)); - - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numbytes; /* no valid m.b. char */ - - for (; numbytes > 0; numbytes--) { - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum++; - ptr += mb_len; - } - - return sum; -#else - return numbytes; -#endif -} @@ -2181,7 +2181,7 @@ yyreduce: if ((n->flags & NUMBER) == 0) yyerror(_("non-numeric value found, numeric expected")); else - negate_num(n); + numbr_hndlr->gawk_negate_number(n); (yyval) = (yyvsp[0]); } #line 2188 "command.c" /* yacc.c:1646 */ @@ -2995,24 +2995,9 @@ err: if (isdigit((unsigned char) tokstart[0])) { NODE *r = NULL; - errno = 0; -#ifdef HAVE_MPFR - if (do_mpfr) { - int tval; - r = mpg_float(); - tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 0, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - if (mpfr_integer_p(r->mpg_numbr)) { - /* integral value, convert to a GMP type. */ - NODE *tmp = r; - r = mpg_integer(); - mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ); - unref(tmp); - } - } else -#endif - r = make_number(strtod(tokstart, & lexptr)); - + /* XXX: not accepting a hex or octal number, maybe should? */ + + r = str2node(tokstart, & lexptr, 0, false); if (errno != 0) { yyerror(strerror(errno)); unref(r); @@ -3212,7 +3197,8 @@ do_help(CMDARG *arg, int cmd) #ifdef HAVE_LIBREADLINE -/* next_word --- find the next word in a line to complete +/* + * next_word --- find the next word in a line to complete * (word seperation characters are space and tab). */ @@ -3238,9 +3224,11 @@ next_word(char *p, int len, char **endp) return p; } -/* command_completion --- attempt to complete based on the word number in line; - * try to complete on command names if this is the first word; for the next - * word(s), the type of completion depends on the command name (first word). + +/* + * command_completion --- attempt to complete based on the word number in line; + * try to complete on command names if this is the first word; for the next + * word(s), the type of completion depends on the command name (first word). */ #ifndef RL_READLINE_VERSION /* < 4.2a */ @@ -686,7 +686,7 @@ node if ((n->flags & NUMBER) == 0) yyerror(_("non-numeric value found, numeric expected")); else - negate_num(n); + numbr_hndlr->gawk_negate_number(n); $$ = $2; } ; @@ -1244,24 +1244,9 @@ err: if (isdigit((unsigned char) tokstart[0])) { NODE *r = NULL; - errno = 0; -#ifdef HAVE_MPFR - if (do_mpfr) { - int tval; - r = mpg_float(); - tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 0, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - if (mpfr_integer_p(r->mpg_numbr)) { - /* integral value, convert to a GMP type. */ - NODE *tmp = r; - r = mpg_integer(); - mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ); - unref(tmp); - } - } else -#endif - r = make_number(strtod(tokstart, & lexptr)); - + /* XXX: not accepting a hex or octal number, maybe should? */ + + r = str2node(tokstart, & lexptr, 0, false); if (errno != 0) { yyerror(strerror(errno)); unref(r); @@ -1461,7 +1446,8 @@ do_help(CMDARG *arg, int cmd) #ifdef HAVE_LIBREADLINE -/* next_word --- find the next word in a line to complete +/* + * next_word --- find the next word in a line to complete * (word seperation characters are space and tab). */ @@ -1487,9 +1473,11 @@ next_word(char *p, int len, char **endp) return p; } -/* command_completion --- attempt to complete based on the word number in line; - * try to complete on command names if this is the first word; for the next - * word(s), the type of completion depends on the command name (first word). + +/* + * command_completion --- attempt to complete based on the word number in line; + * try to complete on command names if this is the first word; for the next + * word(s), the type of completion depends on the command name (first word). */ #ifndef RL_READLINE_VERSION /* < 4.2a */ @@ -40,6 +40,7 @@ extern FILE *output_fp; extern IOBUF *curfile; extern const char *command_file; extern const char *get_spec_varname(Func_ptr fptr); +extern NODE *format_tree(const char *, size_t, NODE **, long); /* format.c */ extern int zzparse(void); #define read_command() (void) zzparse() @@ -3676,27 +3677,13 @@ print_memory(NODE *m, NODE *func, Func_print print_func, FILE *fp) case Node_val: if (m == Nnull_string) print_func(fp, "Nnull_string"); - else if ((m->flags & NUMBER) != 0) { -#ifdef HAVE_MPFR - if ((m->flags & MPFN) != 0) - print_func(fp, "%s", mpg_fmt("%R*g", ROUND_MODE, m->mpg_numbr)); - else if ((m->flags & MPZN) != 0) - print_func(fp, "%s", mpg_fmt("%Zd", m->mpg_i)); - else -#endif - print_func(fp, "%g", m->numbr); - } else if ((m->flags & STRING) != 0) + else if ((m->flags & NUMBER) != 0) + print_func(fp, "%s", fmt_number("%0.17g", m)); + else if ((m->flags & STRING) != 0) pp_string_fp(print_func, fp, m->stptr, m->stlen, '"', false); - else if ((m->flags & NUMCUR) != 0) { -#ifdef HAVE_MPFR - if ((m->flags & MPFN) != 0) - print_func(fp, "%s", mpg_fmt("%R*g", ROUND_MODE, m->mpg_numbr)); - else if ((m->flags & MPZN) != 0) - print_func(fp, "%s", mpg_fmt("%Zd", m->mpg_i)); - else -#endif - print_func(fp, "%g", m->numbr); - } else if ((m->flags & STRCUR) != 0) + else if ((m->flags & NUMCUR) != 0) + print_func(fp, "%s", fmt_number("%0.17g", m)); + else if ((m->flags & STRCUR) != 0) pp_string_fp(print_func, fp, m->stptr, m->stlen, '"', false); else print_func(fp, "-?-"); @@ -3988,12 +3975,6 @@ print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump) case Op_match_rec: case Op_match: case Op_nomatch: - case Op_plus_i: - case Op_minus_i: - case Op_times_i: - case Op_exp_i: - case Op_quotient_i: - case Op_mod_i: case Op_assign_concat: print_memory(pc->memory, func, print_func, fp); /* fall through */ diff --git a/double.c b/double.c new file mode 100644 index 00000000..cfb2fbf9 --- /dev/null +++ b/double.c @@ -0,0 +1,1641 @@ +/* + * double.c - routines for C double support in gawk. + */ + +/* + * Copyright (C) 2012 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" +#include <math.h> +#include "random.h" +#include "floatmagic.h" /* definition of isnan */ + +#include "format.h" + +/* Can declare these, since we always use the random shipped with gawk */ +extern char *initstate(unsigned long seed, char *state, long n); +extern char *setstate(char *state); +extern long random(void); +extern void srandom(unsigned long seed); + +/* + * Since we supply the version of random(), we know what + * value to use here. + */ +#define GAWK_RANDOM_MAX 0x7fffffffL + +/* exported routines */ + +static NODE *make_awknum(AWKNUM); +static int cmp_awknums(const NODE *, const NODE *); +static void negate_awknum(NODE *); +static NODE *str2awknum(char *, char **, int, bool); +static NODE *force_awknum(NODE *); +static NODE *format_awknum_val(const char *, int, NODE *); +static unsigned long awknum_toulong(const NODE *); +static long awknum_tolong(const NODE *); +static double awknum_todouble(const NODE *); +static uintmax_t awknum_touintmax_t(const NODE *); +static int awknum_sgn(const NODE *); +static bool awknum_isinteger(const NODE *); +static bool awknum_isnan(const NODE *); +static bool awknum_isinf(const NODE *); +static NODE *awknum_copy(const NODE *); +static int format_awknum_printf(NODE *, struct format_spec *, struct print_fmt_buf *); +static bool awknum_init(bltin_t **); +static NODE *awknum_add(const NODE *, const NODE *); +static NODE *awknum_sub(const NODE *, const NODE *); +static NODE *awknum_mul(const NODE *, const NODE *); +static NODE *awknum_div(const NODE *, const NODE *); +static NODE *awknum_mod(const NODE *, const NODE *); +static NODE *awknum_pow(const NODE *, const NODE *); +static NODE *awknum_add_long(const NODE *, long); +static NODE *awknum_update_var(NODE *); +static void awknum_set_var(const NODE *); +static long awknum_increment_var(const NODE *, long); +static void awknum_init_vars(void); + +static NODE *do_and(int); +static NODE *do_atan2(int); +static NODE *do_compl(int); +static NODE *do_cos(int); +static NODE *do_exp(int); +static NODE *do_int(int); +static NODE *do_log(int); +static NODE *do_lshift(int); +static NODE *do_or(int); +static NODE *do_rand(int); +static NODE *do_rshift(int); +static NODE *do_sin(int); +static NODE *do_sqrt(int); +static NODE *do_srand(int); +static NODE *do_strtonum(int); +static NODE *do_xor(int); + +/* internal routines */ +static double double_to_int(double d); +static int is_ieee_magic_val(const char *val); +static AWKNUM get_ieee_magic_val(const char *val); +static AWKNUM calc_exp(AWKNUM x1, AWKNUM x2); + + +numbr_handler_t awknum_hndlr = { + awknum_init, + NULL, /* version_str */ + NULL, /* load_procinfo */ + make_awknum, + str2awknum, + awknum_copy, + NULL, /* free_awknum --- not needed for AWKNUM */ + force_awknum, + negate_awknum, + cmp_awknums, + awknum_sgn, + awknum_isinteger, + awknum_isnan, + awknum_isinf, + format_awknum_val, + format_awknum_printf, + awknum_todouble, + awknum_tolong, + awknum_toulong, + awknum_touintmax_t, + awknum_add, + awknum_sub, + awknum_mul, + awknum_div, + awknum_mod, + awknum_pow, + awknum_add_long, + awknum_update_var, + awknum_set_var, + awknum_increment_var, + awknum_init_vars, +}; + +/* awknum_init --- initialization routine */ + +static bool +awknum_init(bltin_t **numbr_bltins) +{ + static bltin_t awknum_bltins[] = { + { "and", do_and }, + { "atan2", do_atan2 }, + { "compl", do_compl }, + { "cos", do_cos }, + { "div", do_div }, + { "exp", do_exp }, + { "int", do_int }, + { "log", do_log }, + { "lshift", do_lshift }, + { "or", do_or }, + { "rand", do_rand }, + { "rshift", do_rshift }, + { "sin", do_sin }, + { "sqrt", do_sqrt }, + { "srand", do_srand }, + { "strtonum", do_strtonum }, + { "xor", do_xor }, + { NULL, NULL }, + }; + + /* set the numeric value of null string */ + Nnull_string->numbr = 0.0; + Nnull_string->flags |= (NUMCUR|NUMBER); + + /* initialize TRUE and FALSE nodes */ + false_node = make_awknum(0.0); + true_node = make_awknum(1.0); + false_node->flags |= NUMINT; + true_node->flags |= NUMINT; + + *numbr_bltins = awknum_bltins; + return true; +} + +/* awknum_toulong --- conversion to unsigned long */ + +static unsigned long +awknum_toulong(const NODE *n) +{ + return n->numbr; +} + +/* awknum_tolong --- conversion to long */ + +static long +awknum_tolong(const NODE *n) +{ + return n->numbr; +} + +/* awknum_todouble --- conversion to AWKNUM */ + +static double +awknum_todouble(const NODE *n) +{ + return n->numbr; +} + +/* awknum_touintmax_t --- conversion to uintmax_t */ + +static uintmax_t +awknum_touintmax_t(const NODE *n) +{ + return n->numbr; +} + +/* awknum_sgn --- return 1 if number > 0, zero if number == 0, and -1 if number < 0 */ + +static int +awknum_sgn(const NODE *n) +{ + return (n->numbr < 0.0 ? -1 : n->numbr > 0.0); +} + +/* awknum_isinteger --- check if a number is an integer */ + +static bool +awknum_isinteger(const NODE *n) +{ + if (isnan(n->numbr) || isinf(n->numbr)) + return false; + return double_to_int(n->numbr) == n->numbr; +} + +/* awknum_isnan --- check if number is NaN */ + +static bool +awknum_isnan(const NODE *n) +{ + return isnan(n->numbr); +} + +/* awknum_isinf --- check if number is infinity */ + +static bool +awknum_isinf(const NODE *n) +{ + return isinf(n->numbr); +} + + +/* negate_awknum --- negate AWKNUM in NODE */ + +static void +negate_awknum(NODE *n) +{ + n->numbr = - n->numbr; +} + +/* awknum_add --- add two numbers */ + +static NODE * +awknum_add(const NODE *t1, const NODE *t2) +{ + return make_awknum(t1->numbr + t2->numbr); +} + +/* awknum_sub --- subtract two numbers */ + +static NODE * +awknum_sub(const NODE *t1, const NODE *t2) +{ + return make_awknum(t1->numbr - t2->numbr); +} + +/* awknum_mul --- multiply two numbers */ + +static NODE * +awknum_mul(const NODE *t1, const NODE *t2) +{ + return make_awknum(t1->numbr * t2->numbr); +} + +/* awknum_add --- quotient of two numbers */ + +static NODE * +awknum_div(const NODE *t1, const NODE *t2) +{ + if (t2->numbr == 0) + fatal(_("division by zero attempted")); + return make_awknum(t1->numbr / t2->numbr); +} + +/* awknum_add_long --- add long value to a number */ + +static NODE * +awknum_add_long(const NODE *t1, long n) +{ + return make_awknum(t1->numbr + n); +} + +/* awknum_copy --- copy a number */ + +static NODE * +awknum_copy(const NODE *t1) +{ + return make_awknum(t1->numbr); +} + +/* awknum_update_var --- update a special variable from internal variables */ + +static NODE * +awknum_update_var(NODE *var) +{ + NODE *val = var->var_value; + + if (var == NR_node) { + if (val->numbr != NR) { + unref(val); + val = NR_node->var_value = make_awknum(NR); + } + return val; + } + + assert(var == FNR_node); + if (val->numbr != FNR) { + unref(val); + val = FNR_node->var_value = make_awknum(FNR); + } + return val; +} + +/* + * awknum_set_vars --- update internal variables for assignment + * to a special variable. + */ + +static void +awknum_set_var(const NODE *var) +{ + NODE *val = var->var_value; + if (var == NR_node) + NR = val->numbr; + else if (var == FNR_node) + FNR = val->numbr; + else { + /* PREC and ROUNMODE */ + if (do_lint) + lintwarn(_("setting `%s' has no effect"), + var == PREC_node ? "PREC" : "ROUNDMODE"); + } +} + +/* awknum_increment_var --- increment NR or FNR */ + +static long +awknum_increment_var(const NODE *var ATTRIBUTE_UNUSED, long nr) +{ + /* var == (F)NR_node */ + return ++nr; +} + +/* awknum_init_vars --- initialize special variables */ + +static void +awknum_init_vars() +{ + unref(PREC_node->var_value); + PREC_node->var_value = make_awknum(DBL_MANT_DIG); + PREC_node->var_value->flags |= NUMINT; + unref(ROUNDMODE_node->var_value); + ROUNDMODE_node->var_value = make_string("N", 1); +} + +/* make_awknum --- allocate a node with defined number */ + +static NODE * +make_awknum(AWKNUM x) +{ + NODE *r; + getnode(r); + r->type = Node_val; + r->numbr = x; + r->flags = MALLOC|NUMBER|NUMCUR; + r->valref = 1; + r->stptr = NULL; + r->stlen = 0; +#if MBS_SUPPORT + r->wstptr = NULL; + r->wstlen = 0; +#endif /* defined MBS_SUPPORT */ + return r; +} + +/* make_integer - Convert an integer to a number node. */ + +static inline NODE * +make_integer(uintmax_t n) +{ + n = adjust_uint(n); + return make_awknum(n); +} + +/* do_lshift --- perform a << operation */ + +static NODE * +do_lshift(int nargs) +{ + NODE *s1, *s2; + uintmax_t uval, ushift, res; + AWKNUM val, shift; + + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { + if ((s1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("lshift: received non-numeric first argument")); + if ((s2->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("lshift: received non-numeric second argument")); + } + val = force_number(s1)->numbr; + shift = force_number(s2)->numbr; + if (do_lint) { + if (val < 0 || shift < 0) + lintwarn(_("lshift(%f, %f): negative values will give strange results"), + (double) val, (double) shift); + if (double_to_int(val) != val || double_to_int(shift) != shift) + lintwarn(_("lshift(%f, %f): fractional values will be truncated"), + (double) val, (double) shift); + if (shift >= sizeof(uintmax_t) * CHAR_BIT) + lintwarn(_("lshift(%f, %f): too large shift value will give strange results"), + (double) val, (double) shift); + } + + DEREF(s1); + DEREF(s2); + + uval = (uintmax_t) val; + ushift = (uintmax_t) shift; + + res = uval << ushift; + return make_integer(res); +} + +/* do_rshift --- perform a >> operation */ + +static NODE * +do_rshift(int nargs) +{ + NODE *s1, *s2; + uintmax_t uval, ushift, res; + AWKNUM val, shift; + + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { + if ((s1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("rshift: received non-numeric first argument")); + if ((s2->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("rshift: received non-numeric second argument")); + } + val = force_number(s1)->numbr; + shift = force_number(s2)->numbr; + if (do_lint) { + if (val < 0 || shift < 0) + lintwarn(_("rshift(%f, %f): negative values will give strange results"), + (double) val, (double) shift); + if (double_to_int(val) != val || double_to_int(shift) != shift) + lintwarn(_("rshift(%f, %f): fractional values will be truncated"), + (double) val, (double) shift); + if (shift >= sizeof(uintmax_t) * CHAR_BIT) + lintwarn(_("rshift(%f, %f): too large shift value will give strange results"), + (double) val, (double) shift); + } + + DEREF(s1); + DEREF(s2); + + uval = (uintmax_t) val; + ushift = (uintmax_t) shift; + + res = uval >> ushift; + return make_integer(res); +} + +/* do_and --- perform an & operation */ + +static NODE * +do_and(int nargs) +{ + NODE *s1; + uintmax_t res, uval; + AWKNUM val; + int i; + + res = ~0; /* start off with all ones */ + if (nargs < 2) + fatal(_("and: called with less than two arguments")); + + for (i = 1; nargs > 0; nargs--, i++) { + s1 = POP_SCALAR(); + if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("and: argument %d is non-numeric"), i); + + val = force_number(s1)->numbr; + if (do_lint && val < 0) + lintwarn(_("and: argument %d negative value %g will give strange results"), + i, (double) val); + + uval = (uintmax_t) val; + res &= uval; + + DEREF(s1); + } + + return make_integer(res); +} + +/* do_or --- perform an | operation */ + +static NODE * +do_or(int nargs) +{ + NODE *s1; + uintmax_t res, uval; + AWKNUM val; + int i; + + res = 0; + if (nargs < 2) + fatal(_("or: called with less than two arguments")); + + for (i = 1; nargs > 0; nargs--, i++) { + s1 = POP_SCALAR(); + if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("or: argument %d is non-numeric"), i); + + val = force_number(s1)->numbr; + if (do_lint && val < 0) + lintwarn(_("or: argument %d negative value %g will give strange results"), + i, (double) val); + + uval = (uintmax_t) val; + res |= uval; + + DEREF(s1); + } + + return make_integer(res); +} + +/* do_xor --- perform an ^ operation */ + +static NODE * +do_xor(int nargs) +{ + NODE *s1; + uintmax_t res, uval; + AWKNUM val; + int i; + + if (nargs < 2) + fatal(_("xor: called with less than two arguments")); + + res = 0; /* silence compiler warning */ + for (i = 1; nargs > 0; nargs--, i++) { + s1 = POP_SCALAR(); + if (do_lint && (s1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("xor: argument %d is non-numeric"), i); + + val = force_number(s1)->numbr; + if (do_lint && val < 0) + lintwarn(_("xor: argument %d negative value %g will give strange results"), + i, (double) val); + + uval = (uintmax_t) val; + if (i == 1) + res = uval; + else + res ^= uval; + + DEREF(s1); + } + + return make_integer(res); +} + +/* do_compl --- perform a ~ operation */ + +static NODE * +do_compl(int nargs) +{ + NODE *tmp; + AWKNUM d; + uintmax_t uval; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("compl: received non-numeric argument")); + d = force_number(tmp)->numbr; + DEREF(tmp); + + if (do_lint) { + if (d < 0) + lintwarn(_("compl(%f): negative value will give strange results"), + (double) d); + if (double_to_int(d) != d) + lintwarn(_("compl(%f): fractional value will be truncated"), + (double) d); + } + + uval = (uintmax_t) d; + uval = ~ uval; + return make_integer(uval); +} + +/* nondec2awknum --- convert octal or hex value to double */ + +/* + * Because of awk's concatenation rules and the way awk.y:yylex() + * collects a number, this routine has to be willing to stop on the + * first invalid character. + */ + +static AWKNUM +nondec2awknum(char *str, size_t len) +{ + AWKNUM retval = 0.0; + char save; + short val; + char *start = str; + + if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) { + /* + * User called strtonum("0x") or some such, + * so just quit early. + */ + if (len <= 2) + return (AWKNUM) 0.0; + + for (str += 2, len -= 2; len > 0; len--, str++) { + switch (*str) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + val = *str - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + val = *str - 'a' + 10; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + val = *str - 'A' + 10; + break; + default: + goto done; + } + retval = (retval * 16) + val; + } + } else if (*str == '0') { + for (; len > 0; len--) { + if (! isdigit((unsigned char) *str)) + goto done; + else if (*str == '8' || *str == '9') { + str = start; + goto decimal; + } + retval = (retval * 8) + (*str - '0'); + str++; + } + } else { +decimal: + save = str[len]; + retval = strtod(str, NULL); + str[len] = save; + } +done: + return retval; +} + + +/* do_rand --- do the rand function */ + +static bool firstrand = true; +/* Some systems require this array to be integer aligned. Sigh. */ +#define SIZEOF_STATE 256 +static uint32_t istate[SIZEOF_STATE/sizeof(uint32_t)]; +static char *const state = (char *const) istate; + +/* ARGSUSED */ +static NODE * +do_rand(int nargs ATTRIBUTE_UNUSED) +{ + double tmprand; +#define RAND_DIVISOR ((double)GAWK_RANDOM_MAX+1.0) + if (firstrand) { + (void) initstate((unsigned) 1, state, SIZEOF_STATE); + /* don't need to srandom(1), initstate() does it for us. */ + firstrand = false; + setstate(state); + } + /* + * Per historical practice and POSIX, return value N is + * + * 0 <= n < 1 + */ + /* + * Date: Wed, 28 Aug 2013 17:52:46 -0700 + * From: Bob Jewett <jewett@bill.scs.agilent.com> + * + * Call random() twice to fill in more bits in the value + * of the double. Also, there is a bug in random() such + * that when the values of successive values are combined + * like (rand1*rand2)^2, (rand3*rand4)^2, ... the + * resulting time series is not white noise. The + * following also seems to fix that bug. + * + * The add/subtract 0.5 keeps small bits from filling + * below 2^-53 in the double, not that anyone should be + * looking down there. + * + * Date: Wed, 25 Sep 2013 10:45:38 -0600 (MDT) + * From: "Nelson H. F. Beebe" <beebe@math.utah.edu> + * (4) The code is typical of many published fragments for converting + * from integer to floating-point, and I discuss the serious pitfalls + * in my book, because it leads to platform-dependent behavior at the + * end points of the interval [0,1] + * + * (5) the documentation in the gawk info node says + * + * `rand()' + * Return a random number. The values of `rand()' are uniformly + * distributed between zero and one. The value could be zero but is + * never one.(1) + * + * The division by RAND_DIVISOR may not guarantee that 1.0 is never + * returned: the programmer forgot the platform-dependent issue of + * rounding. + * + * For points 4 and 5, the safe way is a loop: + * + * double + * rand(void) // return value in [0.0, 1.0) + * { + * value = internal_rand(); + * + * while (value == 1.0) + * value = internal_rand(); + * + * return (value); + * } + */ + + do { + tmprand = 0.5 + ( (random()/RAND_DIVISOR + random()) + / RAND_DIVISOR); + tmprand -= 0.5; + } while (tmprand == 1.0); + + return make_number((AWKNUM) tmprand); +} + +/* do_srand --- seed the random number generator */ + +static NODE * +do_srand(int nargs) +{ + NODE *tmp; + static long save_seed = 1; + long ret = save_seed; /* SVR4 awk srand returns previous seed */ + + if (firstrand) { + (void) initstate((unsigned) 1, state, SIZEOF_STATE); + /* don't need to srandom(1), we're changing the seed below */ + firstrand = false; + (void) setstate(state); + } + + if (nargs == 0) + srandom((unsigned int) (save_seed = (long) time((time_t *) 0))); + else { + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("srand: received non-numeric argument")); + srandom((unsigned int) (save_seed = (long) force_number(tmp)->numbr)); + DEREF(tmp); + } + return make_awknum((AWKNUM) ret); +} + + +/* str2awknum --- create a number node from string */ + +static NODE * +str2awknum(char *str, char **endptr, int base, bool is_integer ATTRIBUTE_UNUSED) +{ + NODE *r; + AWKNUM d; + + if (base == 0) { + /* + * special case -- only used to parse input in the debugger? + * FIXME: reject ieee specials we don't want and/or use the same + * rules when reading numbers from a source file and nuke this case. + */ + + errno = 0; + d = strtod(str, endptr); + if (errno != 0) + d = 0; + } else { + if (base == 8 || base == 16) + d = nondec2awknum(str, strlen(str)); + else + d = atof(str); + } + r = make_awknum(d); + if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) + r->flags |= NUMINT; + return r; +} + +/* awknum_mod --- remainder from division of two numbers */ + +static NODE * +awknum_mod(const NODE *t1, const NODE *t2) +{ + AWKNUM x; + + if (t2->numbr == 0) + fatal(_("division by zero attempted in `%%'")); +#ifdef HAVE_FMOD + x = fmod(t1->numbr, t2->numbr); +#else /* ! HAVE_FMOD */ + (void) modf(t1->numbr / t2->numbr, & x); + x = t1->numbr - x * t2->numbr; +#endif /* ! HAVE_FMOD */ + return make_awknum(x); +} + +/* awknum_pow --- power function */ + +static NODE * +awknum_pow(const NODE *t1, const NODE *t2) +{ + return make_awknum(calc_exp(t1->numbr, t2->numbr)); +} + + +/* + * calc_exp_posint --- calculate x^n for positive integral n, + * using exponentiation by squaring without recursion. + */ + +static AWKNUM +calc_exp_posint(AWKNUM x, long n) +{ + AWKNUM mult = 1; + + while (n > 1) { + if ((n % 2) == 1) + mult *= x; + x *= x; + n /= 2; + } + return mult * x; +} + +/* calc_exp --- calculate x1^x2 */ + +static AWKNUM +calc_exp(AWKNUM x1, AWKNUM x2) +{ + long lx; + + if ((lx = x2) == x2) { /* integer exponent */ + if (lx == 0) + return 1; + return (lx > 0) ? calc_exp_posint(x1, lx) + : 1.0 / calc_exp_posint(x1, -lx); + } + return (AWKNUM) pow((double) x1, (double) x2); +} + +/* cmp_awknums --- compare two AWKNUMs */ + +static int +cmp_awknums(const NODE *t1, const NODE *t2) +{ + /* + * This routine is also used to sort numeric array indices or values. + * For the purposes of sorting, NaN is considered greater than + * any other value, and all NaN values are considered equivalent and equal. + * This isn't in compliance with IEEE standard, but compliance w.r.t. NaN + * comparison at the awk level is a different issue, and needs to be dealt + * with in the interpreter for each opcode seperately. + */ + + if (isnan(t1->numbr)) + return ! isnan(t2->numbr); + if (isnan(t2->numbr)) + return -1; + /* don't subtract, in case one or both are infinite */ + if (t1->numbr == t2->numbr) + return 0; + if (t1->numbr < t2->numbr) + return -1; + return 1; +} + +/* force_awknum --- force a value to be numeric */ + +static NODE * +force_awknum(NODE *n) +{ + char *cp; + char *cpend; + char save; + char *ptr; + unsigned int newflags; + + if ((n->flags & NUMCUR) != 0) + return n; + + /* all the conditionals are an attempt to avoid the expensive strtod */ + + /* Note: only set NUMCUR if we actually convert some digits */ + + n->numbr = 0.0; + + if (n->stlen == 0) { + return n; + } + + cp = n->stptr; + /* + * 2/2007: + * POSIX, by way of severe language lawyering, seems to + * allow things like "inf" and "nan" to mean something. + * So if do_posix, the user gets what he deserves. + * This also allows hexadecimal floating point. Ugh. + */ + if (! do_posix) { + if (isalpha((unsigned char) *cp)) { + return n; + } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) { + if ((n->flags & MAYBE_NUM) != 0) + n->flags &= ~MAYBE_NUM; + n->flags |= NUMBER|NUMCUR; + n->numbr = get_ieee_magic_val(n->stptr); + return n; + } + /* else + fall through */ + } + /* else not POSIX, so + fall through */ + + cpend = cp + n->stlen; + while (cp < cpend && isspace((unsigned char) *cp)) + cp++; + + if ( cp == cpend /* only spaces, or */ + || (! do_posix /* not POSIXLY paranoid and */ + && (isalpha((unsigned char) *cp) /* letter, or */ + /* CANNOT do non-decimal and saw 0x */ + || (! do_non_decimal_data && cp[0] == '0' + && (cp[1] == 'x' || cp[1] == 'X'))))) { + return n; + } + + if ((n->flags & MAYBE_NUM) != 0) { + newflags = NUMBER; + n->flags &= ~MAYBE_NUM; + } else + newflags = 0; + + if (cpend - cp == 1) { /* only one character */ + if (isdigit((unsigned char) *cp)) { /* it's a digit! */ + n->numbr = (AWKNUM)(*cp - '0'); + n->flags |= newflags; + n->flags |= NUMCUR; + if (cp == n->stptr) /* no leading spaces */ + n->flags |= NUMINT; + } + return n; + } + + if (do_non_decimal_data) { /* main.c assures false if do_posix */ + errno = 0; + if (! do_traditional && get_numbase(cp, true) != 10) { + n->numbr = nondec2awknum(cp, cpend - cp); + n->flags |= NUMCUR; + ptr = cpend; + goto finish; + } + } + + errno = 0; + save = *cpend; + *cpend = '\0'; + n->numbr = (AWKNUM) strtod((const char *) cp, &ptr); + + /* POSIX says trailing space is OK for NUMBER */ + while (isspace((unsigned char) *ptr)) + ptr++; + *cpend = save; +finish: + if (errno == 0 && ptr == cpend) { + n->flags |= newflags; + n->flags |= NUMCUR; + } else { + errno = 0; + } + + return n; +} + + +/* + * The following lookup table is used as an optimization in force_string; + * (more complicated) variations on this theme didn't seem to pay off, but + * systematic testing might be in order at some point. + */ +static const char *values[] = { + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", +}; +#define NVAL (sizeof(values)/sizeof(values[0])) + +/* format_awknum_val --- format a numeric value based on format */ + +static NODE * +format_awknum_val(const char *format, int index, NODE *s) +{ + char buf[BUFSIZ]; + char *sp = buf; + double val; + + /* + * 2/2007: Simplify our lives here. Instead of worrying about + * whether or not the value will fit into a long just so we + * can use sprintf("%ld", val) on it, always format it ourselves. + * The only thing to worry about is that integral values always + * format as integers. %.0f does that very well. + * + * 6/2008: Would that things were so simple. Always using %.0f + * imposes a notable performance penalty for applications that + * do a lot of conversion of integers to strings. So, we reinstate + * the old code, but use %.0f for integral values that are outside + * the range of a long. This seems a reasonable compromise. + * + * 12/2009: Use <= and >= in the comparisons with LONG_xxx instead of + * < and > so that things work correctly on systems with 64 bit integers. + */ + + if ((s->flags & STRCUR) != 0) + efree(s->stptr); + free_wstr(s); + + + /* not an integral value, or out of range */ + if ((val = double_to_int(s->numbr)) != s->numbr + || val <= LONG_MIN || val >= LONG_MAX + ) { + struct format_spec spec; + struct print_fmt_buf *outb; + + /* + * Once upon a time, we just blindly did this: + * sprintf(sp, format, s->numbr); + * s->stlen = strlen(sp); + * s->stfmt = (char) index; + * but that's no good if, e.g., OFMT is %s. So we punt, + * and just always format the value ourselves. + */ + + /* XXX: format_spec copied since can be altered in the formatting routine */ + + if (val == s->numbr) { + /* integral value, but outside range of %ld, use %.0f */ + spec = *fmt_list[INT_0f_FMT_INDEX].spec; + s->stfmt = -1; + } else { + assert(fmt_list[index].spec != NULL); /* or can use fmt_parse() --- XXX */ + spec = *fmt_list[index].spec; + s->stfmt = (char) index; + } + + outb = get_fmt_buf(); + (void) format_awknum_printf(s, & spec, outb); + (void) bytes2node(outb, s); + free_fmt_buf(outb); + + s->stptr[s->stlen] = '\0'; + } else { + /* + * integral value; force conversion to long only once. + */ + long num = (long) val; + + if (num < NVAL && num >= 0) { + sp = (char *) values[num]; + s->stlen = 1; + } else { + (void) sprintf(sp, "%ld", num); + s->stlen = strlen(sp); + } + s->stfmt = -1; + if ((s->flags & INTIND) != 0) { + s->flags &= ~(INTIND|NUMBER); + s->flags |= STRING; + } + + emalloc(s->stptr, char *, s->stlen + 2, "format_awknum_val"); + memcpy(s->stptr, sp, s->stlen + 1); + } + + s->flags |= STRCUR; + return s; +} + +/* is_ieee_magic_val --- return true for +inf, -inf, +nan, -nan */ + +static int +is_ieee_magic_val(const char *val) +{ + /* + * Avoid strncasecmp: it mishandles ASCII bytes in some locales. + * Assume the length is 4, as the caller checks this. + */ + return ( (val[0] == '+' || val[0] == '-') + && ( ( (val[1] == 'i' || val[1] == 'I') + && (val[2] == 'n' || val[2] == 'N') + && (val[3] == 'f' || val[3] == 'F')) + || ( (val[1] == 'n' || val[1] == 'N') + && (val[2] == 'a' || val[2] == 'A') + && (val[3] == 'n' || val[3] == 'N')))); +} + +/* get_ieee_magic_val --- return magic value for string */ + +static AWKNUM +get_ieee_magic_val(const char *val) +{ + static bool first = true; + static AWKNUM inf; + static AWKNUM nan; + char *ptr; + AWKNUM v; + + v = strtod(val, & ptr); + + if (val == ptr) { /* Older strtod implementations don't support inf or nan. */ + if (first) { + first = false; + nan = sqrt(-1.0); + inf = -log(0.0); + } + + v = ((val[1] == 'i' || val[1] == 'I') ? inf : nan); + if (val[0] == '-') + v = -v; + } + + return v; +} + +/* double_to_int --- convert double to int, used in several places */ + +static double +double_to_int(double d) +{ + if (d >= 0) + d = floor(d); + else + d = ceil(d); + return d; +} + +/* do_int --- convert double to int for awk */ + +static NODE * +do_int(int nargs) +{ + NODE *tmp; + AWKNUM d; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("int: received non-numeric argument")); + (void) force_number(tmp); + d = tmp->numbr; + DEREF(tmp); + return make_awknum(double_to_int(d)); +} + +/* do_log --- the log function */ + +static NODE * +do_log(int nargs) +{ + NODE *tmp; + AWKNUM d, arg; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("log: received non-numeric argument")); + arg = force_number(tmp)->numbr; + if (arg < 0.0) + warning(_("log: received negative argument %g"), (double) arg); + d = log(arg); + DEREF(tmp); + return make_awknum(d); +} + +/* do_sqrt --- do the sqrt function */ + +static NODE * +do_sqrt(int nargs) +{ + NODE *tmp; + AWKNUM arg; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("sqrt: received non-numeric argument")); + arg = force_number(tmp)->numbr; + DEREF(tmp); + if (arg < 0.0) + warning(_("sqrt: called with negative argument %g"), (double) arg); + return make_awknum(sqrt(arg)); +} + +/* do_exp --- exponential function */ + +static NODE * +do_exp(int nargs) +{ + NODE *tmp; + AWKNUM d, res; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("exp: received non-numeric argument")); + d = force_number(tmp)->numbr; + DEREF(tmp); + errno = 0; + res = exp(d); + if (errno == ERANGE) + warning(_("exp: argument %g is out of range"), (double) d); + return make_awknum(res); +} + + +/* do_atan2 --- do the atan2 function */ + +static NODE * +do_atan2(int nargs) +{ + NODE *t1, *t2; + AWKNUM d1, d2; + + t2 = POP_SCALAR(); + t1 = POP_SCALAR(); + if (do_lint) { + if ((t1->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("atan2: received non-numeric first argument")); + if ((t2->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("atan2: received non-numeric second argument")); + } + d1 = force_number(t1)->numbr; + d2 = force_number(t2)->numbr; + DEREF(t1); + DEREF(t2); + return make_awknum(atan2(d1, d2)); +} + +/* do_sin --- do the sin function */ + +static NODE * +do_sin(int nargs) +{ + NODE *tmp; + AWKNUM d; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("sin: received non-numeric argument")); + d = sin(force_number(tmp)->numbr); + DEREF(tmp); + return make_awknum(d); +} + +/* do_cos --- do the cos function */ + +static NODE * +do_cos(int nargs) +{ + NODE *tmp; + AWKNUM d; + + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("cos: received non-numeric argument")); + d = cos(force_number(tmp)->numbr); + DEREF(tmp); + return make_awknum(d); +} + +/* do_strtonum --- the strtonum function */ + +static NODE * +do_strtonum(int nargs) +{ + NODE *tmp; + AWKNUM d; + + tmp = POP_SCALAR(); + if ((tmp->flags & (NUMBER|NUMCUR)) != 0) + d = force_number(tmp)->numbr; + else if (get_numbase(tmp->stptr, use_lc_numeric) != 10) + d = nondec2awknum(tmp->stptr, tmp->stlen); + else + d = force_number(tmp)->numbr; + + DEREF(tmp); + return make_awknum(d); +} + +/* do_div --- do integer division, return quotient and remainder in dest array */ + +/* + * We define the semantics as: + * numerator = int(numerator) + * denominator = int(denonmator) + * quotient = int(numerator / denomator) + * remainder = int(numerator % denomator) + */ + +NODE * +do_div(int nargs) +{ + NODE *numerator, *denominator, *result; + double num, denom, quotient, remainder; + NODE *sub, **lhs; + + result = POP_PARAM(); + if (result->type != Node_var_array) + fatal(_("div: third argument is not an array")); + assoc_clear(result); + + denominator = POP_SCALAR(); + numerator = POP_SCALAR(); + + if (do_lint) { + if ((numerator->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("div: received non-numeric first argument")); + if ((denominator->flags & (NUMCUR|NUMBER)) == 0) + lintwarn(_("div: received non-numeric second argument")); + } + + (void) force_number(numerator); + (void) force_number(denominator); + num = double_to_int(get_number_d(numerator)); + denom = double_to_int(get_number_d(denominator)); + + if (denom == 0.0) + fatal(_("div: division by zero attempted")); + + quotient = double_to_int(num / denom); + /* + * FIXME: This code is duplicated, factor it out to a + * separate function. + */ +#ifdef HAVE_FMOD + remainder = fmod(num, denom); +#else /* ! HAVE_FMOD */ + (void) modf(num / denom, & remainder); + remainder = num - remainder * denom; +#endif /* ! HAVE_FMOD */ + remainder = double_to_int(remainder); + + sub = make_string("quotient", 8); + lhs = assoc_lookup(result, sub); + unref(*lhs); + *lhs = make_number((AWKNUM) quotient); + + sub = make_string("remainder", 9); + lhs = assoc_lookup(result, sub); + unref(*lhs); + *lhs = make_number((AWKNUM) remainder); + + return make_number((AWKNUM) 0.0); +} + +/* format_awknum_printf --- format a number for (s)printf */ + +static int +format_awknum_printf(NODE *arg, struct format_spec *spec, struct print_fmt_buf *outb) +{ + AWKNUM tmpval; + uintmax_t uval; + bool sgn; + int i, ii, jj; + char *chp, *cp; + char cs1; + int nc; + + static char stackbuf[64]; /* temporary buffer for integer formatting */ + static char *intbuf = stackbuf; + size_t intbuf_size = 64; + +# define CP cpbuf_start(outb) +# define CEND cpbuf_end(outb) +# define CPBUF cpbuf(outb) + + + tmpval = arg->numbr; + spec->fill = space_string; + spec->chbuf = lchbuf; + + cp = CP; + cs1 = spec->fmtchar; + switch (cs1) { + case 'd': + case 'i': + if (isnan(tmpval) || isinf(tmpval)) + goto out_of_range; + else + tmpval = double_to_int(tmpval); + + /* + * ``The result of converting a zero value with a + * precision of zero is no characters.'' + */ + if (spec->have_prec && spec->prec == 0 && tmpval == 0) { + pr_num_tail(cp, spec->prec, spec, outb); + return 0; + } + + if (tmpval < 0) { + tmpval = -tmpval; + sgn = true; + } else { + if (tmpval == -0.0) /* avoid printing -0 */ + tmpval = 0.0; + sgn = false; + } + + /* + * Use snprintf return value to tell if there + * is enough room in the buffer or not. + */ + while ((i = snprintf(intbuf, intbuf_size, "%.0f", tmpval)) >= intbuf_size) { + if (intbuf == stackbuf) + intbuf = NULL; + intbuf_size = i + 1; + erealloc(intbuf, char *, intbuf_size, "format_tree"); + } + + if (i < 1) + goto out_of_range; + + chp = & intbuf[i-1]; + ii = jj = 0; + do { + tmpbuf_prepend(outb, *chp); + chp--; i--; +#if defined(HAVE_LOCALE_H) + if (spec->quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { + if (i) /* only add if more digits coming */ + tmpbuf_prepend(outb, loc.thousands_sep[0]); /* XXX - assumption it's one char */ + if (loc.grouping[ii+1] == 0) + jj = 0; /* keep using current val in loc.grouping[ii] */ + else if (loc.grouping[ii+1] == CHAR_MAX) + spec->quote_flag= false; + else { + ii++; + jj = 0; + } + } +#endif + } while (i > 0); + + + /* add more output digits to match the precision */ + if (spec->have_prec) { + while (CEND - CP < spec->prec) + tmpbuf_prepend(outb, '0'); + } + + if (sgn) + tmpbuf_prepend(outb, '-'); + else if (spec->signchar) + tmpbuf_prepend(outb, spec->signchar); + /* + * When to fill with zeroes is of course not simple. + * First: No zero fill if left-justifying. + * Next: There seem to be two cases: + * A '0' without a precision, e.g. %06d + * A precision with no field width, e.g. %.10d + * Any other case, we don't want to fill with zeroes. + */ + if (! spec->lj + && ((spec->zero_flag && ! spec->have_prec) + || (spec->fw == 0 && spec->have_prec))) + spec->fill = zero_string; + if (spec->prec > spec->fw) + spec->fw = spec->prec; + spec->prec = CEND - CP; + if (spec->fw > spec->prec && ! spec->lj && spec->fill != space_string + && (*CP == '-' || spec->signchar)) { + bchunk_one(outb, CP); + CP++; + spec->prec--; + spec->fw--; + } + cp = CP; + + pr_num_tail(CP, spec->prec, spec, outb); + return 0; + + case 'X': + spec->chbuf = Uchbuf; + /* FALL THROUGH */ + case 'x': + /* FALL THROUGH */ + case 'u': + /* FALL THROUGH */ + case 'o': + /* + * ``The result of converting a zero value with a + * precision of zero is no characters.'' + * + * If I remember the ANSI C standard, though, + * it says that for octal conversions + * the precision is artificially increased + * to add an extra 0 if # is supplied. + * Indeed, in C, + * printf("%#.0o\n", 0); + * prints a single 0. + */ + + if (! spec->alt && spec->have_prec && spec->prec == 0 && tmpval == 0) { + pr_num_tail(cp, spec->prec, spec, outb); + return 0; + } + + if (tmpval < 0) { + uval = (uintmax_t) (intmax_t) tmpval; + if ((AWKNUM)(intmax_t) uval != double_to_int(tmpval)) + goto out_of_range; + } else { + uval = (uintmax_t) tmpval; + if ((AWKNUM) uval != double_to_int(tmpval)) + goto out_of_range; + } + + /* spec->fmtchar = cs1; */ + format_nondecimal(uval, spec, outb); + return 0; + +out_of_range: + /* out of range - emergency use of %g format */ + if (do_lint) + lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), + (double) tmpval, cs1); + cs1 = 'g'; + goto fmt1; + + case 'F': +#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 + cs1 = 'f'; + /* FALL THROUGH */ +#endif + case 'g': + case 'G': + case 'e': + case 'f': + case 'E': +fmt1: + if (! spec->have_prec) + spec->prec = DEFAULT_G_PRECISION; + + chksize(outb, spec->fw + spec->prec + 11); /* 11 == slop */ + cp = CPBUF; /* XXX --- using the temporary prepend-buffer and + * we know it has enough room (>=11). + */ + *cp++ = '%'; + if (spec->lj) + *cp++ = '-'; + if (spec->signchar) + *cp++ = spec->signchar; + if (spec->alt) + *cp++ = '#'; + if (spec->zero_flag) + *cp++ = '0'; + if (spec->quote_flag) + *cp++ = '\''; + +#if defined(LC_NUMERIC) + if (spec->quote_flag && ! use_lc_numeric) + setlocale(LC_NUMERIC, ""); +#endif + + sprintf(cp, "*.*%c", cs1); + while ((nc = snprintf(buf_end(outb), buf_space(outb), CPBUF, + (int) spec->fw, (int) spec->prec, tmpval)) >= buf_space(outb)) + chksize(outb, nc + 1); + +#if defined(LC_NUMERIC) + if (spec->quote_flag && ! use_lc_numeric) + setlocale(LC_NUMERIC, "C"); +#endif + + buf_adjust(outb, nc); /* adjust data and free space in output buffer */ + return 0; + + default: + cant_happen(); + } + + return -1; +#undef CP +#undef CEND +#undef CPBUF +} @@ -34,6 +34,8 @@ long fcall_count = 0; int currule = 0; IOBUF *curfile = NULL; /* current data file */ bool exiting = false; +NODE *true_node; +NODE *false_node; int (*interpret)(INSTRUCTION *); #define MAX_EXEC_HOOKS 10 @@ -48,7 +50,6 @@ int ORSlen; int OFMTidx; int CONVFMTidx; -static NODE *node_Boolean[2]; /* This rather ugly macro is for VMS C */ #ifdef C @@ -265,18 +266,18 @@ static struct optypetab { char *operator; } optypes[] = { { "Op_illegal", NULL }, + { "Op_plus", " + " }, + { "Op_minus", " - " }, { "Op_times", " * " }, - { "Op_times_i", " * " }, { "Op_quotient", " / " }, - { "Op_quotient_i", " / " }, { "Op_mod", " % " }, - { "Op_mod_i", " % " }, - { "Op_plus", " + " }, - { "Op_plus_i", " + " }, - { "Op_minus", " - " }, - { "Op_minus_i", " - " }, { "Op_exp", " ^ " }, - { "Op_exp_i", " ^ " }, + { "Op_assign_plus", " += " }, + { "Op_assign_minus", " -= " }, + { "Op_assign_times", " *= " }, + { "Op_assign_quotient", " /= " }, + { "Op_assign_mod", " %= " }, + { "Op_assign_exp", " ^= " }, { "Op_concat", " " }, { "Op_line_range", NULL }, { "Op_cond_pair", ", " }, @@ -286,6 +287,7 @@ static struct optypetab { { "Op_predecrement", "--" }, { "Op_postincrement", "++" }, { "Op_postdecrement", "--" }, + { "Op_unary_plus", "+" }, { "Op_unary_minus", "-" }, { "Op_field_spec", "$" }, { "Op_not", "! " }, @@ -293,12 +295,6 @@ static struct optypetab { { "Op_store_var", " = " }, { "Op_store_sub", " = " }, { "Op_store_field", " = " }, - { "Op_assign_times", " *= " }, - { "Op_assign_quotient", " /= " }, - { "Op_assign_mod", " %= " }, - { "Op_assign_plus", " += " }, - { "Op_assign_minus", " -= " }, - { "Op_assign_exp", " ^= " }, { "Op_assign_concat", " " }, { "Op_and", " && " }, { "Op_and_final", NULL }, @@ -850,16 +846,12 @@ set_ORS() /* fmt_ok --- is the conversion format a valid one? */ -NODE **fmt_list = NULL; -static int fmt_ok(NODE *n); +struct fmt_list_item *fmt_list = NULL; static int fmt_index(NODE *n); -static int -fmt_ok(NODE *n) +bool +fmt_ok(const char *p) { - NODE *tmp = force_string(n); - const char *p = tmp->stptr; - #if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 static const char float_formats[] = "efgEG"; #else @@ -872,22 +864,22 @@ fmt_ok(NODE *n) #endif if (*p++ != '%') - return 0; + return false; while (*p && strchr(flags, *p) != NULL) /* flags */ p++; while (*p && isdigit((unsigned char) *p)) /* width - %*.*g is NOT allowed */ p++; if (*p == '\0' || (*p != '.' && ! isdigit((unsigned char) *p))) - return 0; + return false; if (*p == '.') p++; while (*p && isdigit((unsigned char) *p)) /* precision */ p++; if (*p == '\0' || strchr(float_formats, *p) == NULL) - return 0; + return false; if (*++p != '\0') - return 0; - return 1; + return false; + return true; } /* fmt_index --- track values of OFMT and CONVFMT to keep semantics correct */ @@ -896,30 +888,77 @@ static int fmt_index(NODE *n) { int ix = 0; - static int fmt_num = 4; + static int fmt_num = 6; static int fmt_hiwater = 0; + struct format_spec *spec = NULL; + extern struct format_spec *fmt_parse(NODE *, const char *, size_t); + + /* + * BEGIN { CONVFMT="%s"; print 1.1234567 "X" } + * number -> force_string -> CONVFMT = %s -> force_string -> ... + */ + + if (fmt_list == NULL) { + NODE *t; + + emalloc(fmt_list, struct fmt_list_item *, fmt_num * sizeof(*fmt_list), "fmt_index"); + + /* + * XXX: Insert "%d" and "%.0f" at known indices, used to convert integers. + * The indices are defined using `enum { INT_d_FMT_INDEX, INT_0f_FMT_INDEX }' + * in format.h; + * We want to avoid calling format_tree() directly in case the current + * number handler hasn't been initialized, or we need a different handler; + * may also be a bit more efficient. + */ + + t = make_string("%d", 2); + spec = fmt_parse(CONVFMT_node, t->stptr, t->stlen); + assert(spec != NULL); + fmt_list[fmt_hiwater].fmt = t; + fmt_list[fmt_hiwater].spec = spec; + fmt_hiwater++; + + t = make_string("%.0f", 4); + spec = fmt_parse(CONVFMT_node, t->stptr, t->stlen); + assert(spec != NULL); + fmt_list[fmt_hiwater].fmt = t; + fmt_list[fmt_hiwater].spec = spec; + fmt_hiwater++; + } - if (fmt_list == NULL) - emalloc(fmt_list, NODE **, fmt_num*sizeof(*fmt_list), "fmt_index"); n = force_string(n); while (ix < fmt_hiwater) { - if (cmp_nodes(fmt_list[ix], n) == 0) + if (cmp_nodes(fmt_list[ix].fmt, n) == 0) return ix; ix++; } /* not found */ + n->stptr[n->stlen] = '\0'; - if (do_lint && ! fmt_ok(n)) + if (do_lint && ! fmt_ok(n->stptr)) lintwarn(_("bad `%sFMT' specification `%s'"), n == CONVFMT_node->var_value ? "CONV" : n == OFMT_node->var_value ? "O" : "", n->stptr); + if (n == CONVFMT_node->var_value) { + /* XXX -- %s or %c not allowed for CONVFMT */ + spec = fmt_parse(CONVFMT_node, n->stptr, n->stlen); + if (spec == NULL) + fatal(_("invalid CONVFMT specification `%s'"), n->stptr); + } else if (n == OFMT_node->var_value) { + spec = fmt_parse(OFMT_node, n->stptr, n->stlen); + if (spec == NULL) + fatal(_("invalid OFMT specification `%s'"), n->stptr); + } + if (fmt_hiwater >= fmt_num) { fmt_num *= 2; - erealloc(fmt_list, NODE **, fmt_num * sizeof(*fmt_list), "fmt_index"); + erealloc(fmt_list, struct fmt_list_item *, fmt_num * sizeof(*fmt_list), "fmt_index"); } - fmt_list[fmt_hiwater] = dupnode(n); + fmt_list[fmt_hiwater].fmt = dupnode(n); + fmt_list[fmt_hiwater].spec = spec; return fmt_hiwater++; } @@ -929,7 +968,7 @@ void set_OFMT() { OFMTidx = fmt_index(OFMT_node->var_value); - OFMT = fmt_list[OFMTidx]->stptr; + OFMT = fmt_list[OFMTidx].fmt->stptr; } /* set_CONVFMT --- track CONVFMT correctly */ @@ -938,7 +977,7 @@ void set_CONVFMT() { CONVFMTidx = fmt_index(CONVFMT_node->var_value); - CONVFMT = fmt_list[CONVFMTidx]->stptr; + CONVFMT = fmt_list[CONVFMTidx].fmt->stptr; } /* set_LINT --- update LINT as appropriate */ @@ -1019,6 +1058,23 @@ set_TEXTDOMAIN() */ } +/* set_PREC --- tarck PREC correctly */ + +void +set_PREC() +{ + numbr_hndlr->set_numvar(PREC_node); +} + +/* set_ROUNDMODE --- track ROUNDMODE correctly */ + +void +set_ROUNDMODE() +{ + numbr_hndlr->set_numvar(ROUNDMODE_node); +} + + /* update_ERRNO_int --- update the value of ERRNO based on argument */ void @@ -1058,15 +1114,7 @@ unset_ERRNO(void) void update_NR() { -#ifdef HAVE_MPFR - if (is_mpg_number(NR_node->var_value)) - (void) mpg_update_var(NR_node); - else -#endif - if (NR_node->var_value->numbr != NR) { - unref(NR_node->var_value); - NR_node->var_value = make_number(NR); - } + (void) numbr_hndlr->update_numvar(NR_node); } /* update_NF --- update the value of NF */ @@ -1090,15 +1138,7 @@ update_NF() void update_FNR() { -#ifdef HAVE_MPFR - if (is_mpg_number(FNR_node->var_value)) - (void) mpg_update_var(FNR_node); - else -#endif - if (FNR_node->var_value->numbr != FNR) { - unref(FNR_node->var_value); - FNR_node->var_value = make_number(FNR); - } + (void) numbr_hndlr->update_numvar(FNR_node); } @@ -1216,42 +1256,6 @@ r_get_field(NODE *n, Func_ptr *assign, bool reference) } -/* - * calc_exp_posint --- calculate x^n for positive integral n, - * using exponentiation by squaring without recursion. - */ - -static AWKNUM -calc_exp_posint(AWKNUM x, long n) -{ - AWKNUM mult = 1; - - while (n > 1) { - if ((n % 2) == 1) - mult *= x; - x *= x; - n /= 2; - } - return mult * x; -} - -/* calc_exp --- calculate x1^x2 */ - -AWKNUM -calc_exp(AWKNUM x1, AWKNUM x2) -{ - long lx; - - if ((lx = x2) == x2) { /* integer exponent */ - if (lx == 0) - return 1; - return (lx > 0) ? calc_exp_posint(x1, lx) - : 1.0 / calc_exp_posint(x1, -lx); - } - return (AWKNUM) pow((double) x1, (double) x2); -} - - /* setup_frame --- setup new frame for function call */ static INSTRUCTION * @@ -1527,10 +1531,10 @@ unwind_stack(long n) static inline int eval_condition(NODE *t) { - if (t == node_Boolean[false]) + if (t == false_node) return false; - if (t == node_Boolean[true]) + if (t == true_node) return true; if ((t->flags & MAYBE_NUM) != 0) @@ -1570,63 +1574,43 @@ static void op_assign(OPCODE op) { NODE **lhs; - NODE *t1, *t2; - AWKNUM x = 0.0, x1, x2; + NODE *r, *t1, *t2; lhs = POP_ADDRESS(); t1 = *lhs; - x1 = force_number(t1)->numbr; + (void) force_number(t1); - t2 = TOP_SCALAR(); - x2 = force_number(t2)->numbr; - DEREF(t2); + t2 = TOP_NUMBER(); switch (op) { case Op_assign_plus: - x = x1 + x2; + r = numbr_hndlr->add(t1, t2); break; case Op_assign_minus: - x = x1 - x2; + r = numbr_hndlr->sub(t1, t2); break; case Op_assign_times: - x = x1 * x2; + r = numbr_hndlr->mul(t1, t2); break; case Op_assign_quotient: - if (x2 == (AWKNUM) 0) { - decr_sp(); - fatal(_("division by zero attempted in `/='")); - } - x = x1 / x2; + r = numbr_hndlr->div(t1, t2); break; case Op_assign_mod: - if (x2 == (AWKNUM) 0) { - decr_sp(); - fatal(_("division by zero attempted in `%%='")); - } -#ifdef HAVE_FMOD - x = fmod(x1, x2); -#else /* ! HAVE_FMOD */ - (void) modf(x1 / x2, &x); - x = x1 - x2 * x; -#endif /* ! HAVE_FMOD */ + r = numbr_hndlr->mod(t1, t2); break; case Op_assign_exp: - x = calc_exp((double) x1, (double) x2); + r = numbr_hndlr->pow(t1, t2); break; default: - break; - } - - if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { - /* optimization */ - t1->numbr = x; - } else { - unref(t1); - t1 = *lhs = make_number(x); + r = NULL; /* keep warnings quiet */ + cant_happen(); } - UPREF(t1); - REPLACE(t1); + DEREF(t2); + unref(*lhs); + *lhs = r; + UPREF(r); + REPLACE(r); } /* PUSH_CODE --- push a code onto the runtime stack */ @@ -1766,7 +1750,7 @@ register_exec_hook(Func_pre_exec preh, Func_post_exec posth) /* interpreter routine when not debugging */ #include "interpret.h" -/* interpreter routine with exec hook(s). Used when debugging and/or with MPFR. */ +/* interpreter routine with exec hook(s). Used when debugging */ #define r_interpret h_interpret #define EXEC_HOOK 1 #include "interpret.h" @@ -1794,14 +1778,6 @@ init_interpret() frame_ptr->num_tail_calls = 0; frame_ptr->vname = NULL; - /* initialize true and false nodes */ - node_Boolean[false] = make_number(0.0); - node_Boolean[true] = make_number(1.0); - if (! is_mpg_number(node_Boolean[false])) { - node_Boolean[false]->flags |= NUMINT; - node_Boolean[true]->flags |= NUMINT; - } - /* * Select the interpreter routine. The version without * any exec hook support (r_interpret) is faster by about @@ -196,28 +196,24 @@ rebuild_record() */ for (cops = ops, i = 1; i <= NF; i++) { NODE *r = fields_arr[i]; + if (r->stlen > 0) { NODE *n; - getnode(n); if ((r->flags & FIELD) == 0) { - *n = *Null_field; - n->stlen = r->stlen; if ((r->flags & (NUMCUR|NUMBER)) != 0) { - n->flags |= (r->flags & (MPFN|MPZN|NUMCUR|NUMBER)); -#ifdef HAVE_MPFR - if (is_mpg_float(r)) { - mpfr_init(n->mpg_numbr); - mpfr_set(n->mpg_numbr, r->mpg_numbr, ROUND_MODE); - } else if (is_mpg_integer(r)) { - mpz_init(n->mpg_i); - mpz_set(n->mpg_i, r->mpg_i); - } else -#endif - n->numbr = r->numbr; + n = numbr_hndlr->gawk_copy_number(r); + n->flags |= Null_field->flags; + n->flags &= ~MALLOC; + } else { + getnode(n); + *n = *Null_field; } + n->stlen = r->stlen; } else { + getnode(n); *n = *r; + assert((n->flags & (NUMCUR|NUMBER)) == 0); n->flags &= ~(MALLOC|STRING); } diff --git a/format.c b/format.c new file mode 100644 index 00000000..2322d563 --- /dev/null +++ b/format.c @@ -0,0 +1,791 @@ +/* + * format.c - routines for (s)printf formatting. + */ + +/* + * Copyright (C) 2012 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" + +#include "format.h" + +char lchbuf[] = "0123456789abcdef"; +char Uchbuf[] = "0123456789ABCDEF"; +char space_string[] = " "; +char zero_string[] = "0"; + +/* mbc_byte_count --- return number of bytes for corresponding numchars multibyte characters */ + +static size_t +mbc_byte_count(const char *ptr, size_t numchars) +{ +#if MBS_SUPPORT + mbstate_t cur_state; + size_t sum = 0; + int mb_len; + + memset(& cur_state, 0, sizeof(cur_state)); + + assert(gawk_mb_cur_max > 1); + mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); + if (mb_len <= 0) + return numchars; /* no valid m.b. char */ + + for (; numchars > 0; numchars--) { + mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); + if (mb_len <= 0) + break; + sum += mb_len; + ptr += mb_len; + } + + return sum; +#else + return numchars; +#endif +} + +/* mbc_char_count --- return number of m.b. chars in string, up to numbytes bytes */ + +static size_t +mbc_char_count(const char *ptr, size_t numbytes) +{ +#if MBS_SUPPORT + mbstate_t cur_state; + size_t sum = 0; + int mb_len; + + if (gawk_mb_cur_max == 1) + return numbytes; + + memset(& cur_state, 0, sizeof(cur_state)); + + mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); + if (mb_len <= 0) + return numbytes; /* no valid m.b. char */ + + for (; numbytes > 0; numbytes--) { + mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); + if (mb_len <= 0) + break; + sum++; + ptr += mb_len; + } + + return sum; +#else + return numbytes; +#endif +} + + +#define OUTBUF_INIT_SIZE 510 + +/* chksize__internal --- make room for something LEN big in the output buffer */ + +static void +chksize__internal(struct print_fmt_buf *outb, size_t len) +{ + size_t delta, newsize; + + assert(outb->buf != NULL); + delta = outb->dataend - outb->buf; + newsize = delta + len + OUTBUF_INIT_SIZE; + erealloc(outb->buf, char *, newsize + 2, "chksize__internal"); + outb->dataend = outb->buf + delta; + outb->bufsize = newsize; + outb->room_left = len + OUTBUF_INIT_SIZE; +} + +/* cpbuf_chksize__internal --- enlarge the temporary buffer */ + +static void +cpbuf_chksize__internal(struct print_fmt_buf *outb) +{ + char *cp, *prev = outb->cpbuf.buf; + size_t oldsize = outb->cpbuf.bufsize; + size_t newsize; + + newsize = outb->cpbuf.bufsize = 2 * oldsize; + emalloc(outb->cpbuf.buf, char *, newsize + 2, "cpbuf_chksize__internal"); + memcpy((cp = outb->cpbuf.buf + oldsize), prev, oldsize); + efree(prev); + outb->cpbuf.bufend = outb->cpbuf.buf + newsize; + outb->cpbuf.databegin = cp; +} + +static struct print_fmt_buf static_outb; + +/* get_fmt_buf --- buffer(s) to manage (s)printf formatting */ + +struct print_fmt_buf * +get_fmt_buf() +{ + struct print_fmt_buf *outb; + + if (static_outb.buf == NULL) + outb = & static_outb; + else { + emalloc(outb, struct print_fmt_buf *, sizeof (struct print_fmt_buf), "get_fmt_buf"); + outb->is_malloced = true; + } + + emalloc(outb->buf, char *, OUTBUF_INIT_SIZE + 2, "get_fmt_buf"); + outb->bufsize = OUTBUF_INIT_SIZE; + outb->room_left = outb->bufsize; + outb->dataend = outb->buf; + outb->chksize = chksize__internal; + + emalloc(outb->cpbuf.buf, char *, 64, "get_fmt_buf"); + outb->cpbuf.bufsize = 62; + outb->cpbuf.databegin = outb->cpbuf.bufend = outb->cpbuf.buf + outb->cpbuf.bufsize; + outb->cpbuf_chksize = cpbuf_chksize__internal; + return outb; +} + +/* fmt_parse --- parse a single format code */ + +struct format_spec * +fmt_parse(NODE *n, const char *fmt_string, size_t fmt_len) +{ + struct format_spec *spec = NULL; + + spec = (struct format_spec *) format_tree(fmt_string, fmt_len, NULL, LONG_MIN); + if (spec != NULL && n == CONVFMT_node + && (spec->fmtchar == 's' || spec->fmtchar == 'c') + ) { + efree(spec); + spec = NULL; + } + return spec; +} + + +/* format_nondecimal --- output a nondecimal number according to a format */ + +void +format_nondecimal(uintmax_t val, struct format_spec *spec, struct print_fmt_buf *outb) +{ + uintmax_t uval = val; + int ii, jj; + const char *chbuf = spec->chbuf; + +# define CP cpbuf_start(outb) +# define CEND cpbuf_end(outb) + + /* + * When to fill with zeroes is of course not simple. + * First: No zero fill if left-justifying. + * Next: There seem to be two cases: + * A '0' without a precision, e.g. %06d + * A precision with no field width, e.g. %.10d + * Any other case, we don't want to fill with zeroes. + */ + if (! spec->lj + && ((spec->zero_flag && ! spec->have_prec) + || (spec->fw == 0 && spec->have_prec)) + ) + spec->fill = zero_string; + + ii = jj = 0; + do { + tmpbuf_prepend(outb, chbuf[uval % spec->base]); + uval /= spec->base; +#if defined(HAVE_LOCALE_H) + if (spec->base == 10 && spec->quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { + if (uval) /* only add if more digits coming */ + tmpbuf_prepend(outb, loc.thousands_sep[0]); /* XXX --- assumption it's one char */ + if (loc.grouping[ii+1] == 0) + jj = 0; /* keep using current val in loc.grouping[ii] */ + else if (loc.grouping[ii+1] == CHAR_MAX) + spec->quote_flag= false; + else { + ii++; + jj = 0; + } + } +#endif + } while (uval > 0); + + /* add more output digits to match the precision */ + if (spec->have_prec) { + while (CEND - CP < spec->prec) + tmpbuf_prepend(outb, '0'); + } + + if (spec->alt && val != 0) { + if (spec->base == 16) { + tmpbuf_prepend(outb, spec->fmtchar); + tmpbuf_prepend(outb, '0'); + if (spec->fill != space_string) { + bchunk(outb, CP, 2); + CP += 2; + spec->fw -= 2; + } + } else if (spec->base == 8) + tmpbuf_prepend(outb, '0'); + } + + spec->base = 0; + if (spec->prec > spec->fw) + spec->fw = spec->prec; + spec->prec = CEND - CP; + pr_num_tail(CP, spec->prec, spec, outb); + +#undef CP +#undef CEND +} + + +/* + * format_tree() formats arguments of sprintf, + * and accordingly to a fmt_string providing a format like in + * printf family from C library. Returns a string node which value + * is a formatted string. Called by sprintf function. + * + * It is one of the uglier parts of gawk. Thanks to Michal Jaegermann + * for taming this beast and making it compatible with ANSI C. + */ + + +NODE * +format_tree( + const char *fmt_string, + size_t n0, + NODE **the_args, + long num_args) +{ + size_t cur_arg = 0; + NODE *retval = NULL; + bool toofew = false; + const char *s0, *s1; + int cs1; + NODE *arg; + long argnum; + + bool used_dollar; + bool big_flag, bigbig_flag, small_flag, need_format; + long *cur = NULL; + uintmax_t uval; + char *cp; + size_t copy_count, char_count; + + struct print_fmt_buf *outb; + struct format_spec spec; + +# define CP cpbuf_start(outb) +# define CEND cpbuf_end(outb) +# define CPBUF cpbuf(outb) + +/* parse a single format specifier */ +#define do_parse_fmt (num_args == LONG_MIN) + + /* + * Check first for use of `count$'. + * If plain argument retrieval was used earlier, choke. + * Otherwise, return the requested argument. + * If not `count$' now, but it was used earlier, choke. + * If this format is more than total number of args, choke. + * Otherwise, return the current argument. + */ +#define parse_next_arg() { \ + if (do_parse_fmt) \ + goto out; \ + else if (argnum > 0) { \ + if (cur_arg > 1) { \ + msg(_("fatal: must use `count$' on all formats or none")); \ + goto out; \ + } \ + arg = the_args[argnum]; \ + } else if (used_dollar) { \ + msg(_("fatal: must use `count$' on all formats or none")); \ + arg = 0; /* shutup the compiler */ \ + goto out; \ + } else if (cur_arg >= num_args) { \ + arg = 0; /* shutup the compiler */ \ + toofew = true; \ + break; \ + } else { \ + arg = the_args[cur_arg]; \ + cur_arg++; \ + } \ +} + + outb = get_fmt_buf(); + cur_arg = 1; + + need_format = false; + used_dollar = false; + + s0 = s1 = fmt_string; + + while (n0-- > 0) { + if (*s1 != '%') { + s1++; + continue; + } + need_format = true; + bchunk(outb, s0, s1 - s0); + s0 = s1; + argnum = 0; + + memset(& spec, '\0', sizeof (spec)); + cur = & spec.fw; + spec.fill = space_string; /* always space for string */ + + big_flag = bigbig_flag = small_flag = false; + CP = CEND; + s1++; + +retry: + if (n0-- == 0) /* ran out early! */ + break; + + switch (cs1 = *s1++) { + case (-1): /* dummy case to allow for checking */ +check_pos: + if (cur != & spec.fw) + break; /* reject as a valid format */ + goto retry; + case '%': + need_format = false; + /* + * 29 Oct. 2002: + * The C99 standard pages 274 and 279 seem to imply that + * since there's no arg converted, the field width doesn't + * apply. The code already was that way, but this + * comment documents it, at least in the code. + */ + if (do_lint) { + const char *msg = NULL; + + if (spec.fw && ! spec.have_prec) + msg = _("field width is ignored for `%%' specifier"); + else if (spec.fw == 0 && spec.have_prec) + msg = _("precision is ignored for `%%' specifier"); + else if (spec.fw && spec.have_prec) + msg = _("field width and precision are ignored for `%%' specifier"); + + if (msg != NULL) + lintwarn("%s", msg); + } + bchunk_one(outb, "%"); + s0 = s1; + break; + + case '0': + /* + * Only turn on zero_flag if we haven't seen + * the field width or precision yet. Otherwise, + * screws up floating point formatting. + */ + if (cur == & spec.fw) + spec.zero_flag = true; + if (spec.lj) + goto retry; + /* FALL through */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (cur == NULL) + break; + if (spec.prec >= 0) + *cur = cs1 - '0'; + /* + * with a negative precision *cur is already set + * to -1, so it will remain negative, but we have + * to "eat" precision digits in any case + */ + while (n0 > 0 && *s1 >= '0' && *s1 <= '9') { + --n0; + *cur = *cur * 10 + *s1++ - '0'; + } + if (spec.prec < 0) /* negative precision is discarded */ + spec.have_prec = false; + if (cur == & spec.prec) + cur = NULL; + if (n0 == 0) /* badly formatted control string */ + continue; + goto retry; + case '$': + if (do_traditional) { + msg(_("fatal: `$' is not permitted in awk formats")); + goto out; + } + if (do_parse_fmt) + goto out; + + if (cur == & spec.fw) { + argnum = spec.fw; + spec.fw = 0; + used_dollar = true; + if (argnum <= 0) { + msg(_("fatal: arg count with `$' must be > 0")); + goto out; + } + if (argnum >= num_args) { + msg(_("fatal: arg count %ld greater than total number of supplied arguments"), argnum); + goto out; + } + } else { + msg(_("fatal: `$' not permitted after period in format")); + goto out; + } + + goto retry; + case '*': + if (cur == NULL) + break; + if (! do_traditional && isdigit((unsigned char) *s1)) { + int val = 0; + + for (; n0 > 0 && *s1 && isdigit((unsigned char) *s1); s1++, n0--) { + val *= 10; + val += *s1 - '0'; + } + if (*s1 != '$') { + msg(_("fatal: no `$' supplied for positional field width or precision")); + goto out; + } else { + s1++; + n0--; + } + if (val >= num_args) { + toofew = true; + break; + } + arg = the_args[val]; + } else { + parse_next_arg(); + } + + (void) force_number(arg); + *cur = get_number_si(arg); + if (*cur < 0 && cur == & spec.fw) { + *cur = -*cur; + spec.lj++; + } + if (cur == & spec.prec) { + if (*cur >= 0) + spec.have_prec = true; + else + spec.have_prec = false; + cur = NULL; + } + goto retry; + case ' ': /* print ' ' or '-' */ + /* 'space' flag is ignored */ + /* if '+' already present */ + if (spec.signchar != false) + goto check_pos; + /* FALL THROUGH */ + case '+': /* print '+' or '-' */ + spec.signchar = cs1; + goto check_pos; + case '-': + if (spec.prec < 0) + break; + if (cur == & spec.prec) { + spec.prec = -1; + goto retry; + } + spec.lj++; /* filling is ignored */ + goto check_pos; + case '.': + if (cur != & spec.fw) + break; + cur = & spec.prec; + spec.have_prec = true; + goto retry; + case '#': + spec.alt = true; + goto check_pos; + case '\'': +#if defined(HAVE_LOCALE_H) + /* allow quote_flag if there is a thousands separator. */ + if (loc.thousands_sep[0] != '\0') + spec.quote_flag= true; + goto check_pos; +#else + goto retry; +#endif + case 'l': + if (big_flag) + break; + else { + static bool warned = false; + + if (do_lint && ! warned) { + lintwarn(_("`l' is meaningless in awk formats; ignored")); + warned = true; + } + if (do_posix) { + msg(_("fatal: `l' is not permitted in POSIX awk formats")); + goto out; + } + } + big_flag = true; + goto retry; + case 'L': + if (bigbig_flag) + break; + else { + static bool warned = false; + + if (do_lint && ! warned) { + lintwarn(_("`L' is meaningless in awk formats; ignored")); + warned = true; + } + if (do_posix) { + msg(_("fatal: `L' is not permitted in POSIX awk formats")); + goto out; + } + } + bigbig_flag = true; + goto retry; + case 'h': + if (small_flag) + break; + else { + static bool warned = false; + + if (do_lint && ! warned) { + lintwarn(_("`h' is meaningless in awk formats; ignored")); + warned = true; + } + if (do_posix) { + msg(_("fatal: `h' is not permitted in POSIX awk formats")); + goto out; + } + } + small_flag = true; + goto retry; + case 'c': + need_format = false; + spec.fmtchar = cs1; + parse_next_arg(); + /* user input that looks numeric is numeric */ + if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM) + (void) force_number(arg); + if ((arg->flags & NUMBER) != 0) { + uval = get_number_uj(arg); +#if MBS_SUPPORT + if (gawk_mb_cur_max > 1) { + char buf[100]; + wchar_t wc; + mbstate_t mbs; + size_t count; + + memset(& mbs, 0, sizeof(mbs)); + + /* handle systems with too small wchar_t */ + if (sizeof(wchar_t) < 4 && uval > 0xffff) { + if (do_lint) + lintwarn( + _("[s]printf: value %g is too big for %%c format"), + arg->numbr); + + goto out0; + } + + wc = uval; + + count = wcrtomb(buf, wc, & mbs); + if (count == 0 + || count == (size_t) -1) { + if (do_lint) + lintwarn( + _("[s]printf: value %g is not a valid wide character"), + arg->numbr); + + goto out0; + } + + memcpy(CPBUF, buf, count); + spec.prec = count; + cp = CPBUF; + goto pr_tail; + } +out0: + ; + /* else, + fall through */ +#endif + CPBUF[0] = uval; + spec.prec = 1; + cp = CPBUF; + goto pr_tail; + } + /* + * As per POSIX, only output first character of a + * string value. Thus, we ignore any provided + * precision, forcing it to 1. (Didn't this + * used to work? 6/2003.) + */ + cp = arg->stptr; + spec.prec = 1; +#if MBS_SUPPORT + /* + * First character can be multiple bytes if + * it's a multibyte character. Grr. + */ + if (gawk_mb_cur_max > 1) { + mbstate_t state; + size_t count; + + memset(& state, 0, sizeof(state)); + count = mbrlen(cp, arg->stlen, & state); + if (count != (size_t) -1 && count != (size_t) -2 && count > 0) { + spec.prec = count; + /* may need to increase fw so that padding happens, see pr_tail code */ + if (spec.fw > 0) + spec.fw += count - 1; + } + } +#endif + goto pr_tail; + case 's': + need_format = false; + spec.fmtchar = cs1; + parse_next_arg(); + arg = force_string(arg); + if (spec.fw == 0 && ! spec.have_prec) + spec.prec = arg->stlen; + else { + char_count = mbc_char_count(arg->stptr, arg->stlen); + if (! spec.have_prec || spec.prec > char_count) + spec.prec = char_count; + } + cp = arg->stptr; + pr_tail: + if (! spec.lj) + pr_fill(& spec, outb); + + copy_count = spec.prec; + if (spec.fw == 0 && ! spec.have_prec) + ; + else if (gawk_mb_cur_max > 1) { + if (cs1 == 's') { + assert(cp == arg->stptr || cp == CPBUF); + copy_count = mbc_byte_count(arg->stptr, spec.prec); + } + /* prec was set by code for %c */ + /* else + copy_count = prec; */ + } + + bchunk(outb, cp, copy_count); + pr_fill(& spec, outb); + + s0 = s1; + break; + + case 'X': + /* FALL THROUGH */ + case 'x': + spec.base += 6; + /* FALL THROUGH */ + case 'u': + spec.base += 2; + /* FALL THROUGH */ + case 'o': + spec.base += 8; + goto fmt1; + case 'F': +#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 + cs1 = 'f'; + /* FALL THROUGH */ +#endif + case 'g': + case 'G': + case 'e': + case 'f': + case 'E': + case 'd': + case 'i': + fmt1: + need_format = false; + spec.fmtchar = cs1; + parse_next_arg(); + (void) force_number(arg); + spec.fmtchar = cs1; + if (format_number_printf(arg, & spec, outb) < 0) + goto out; + + s0 = s1; + break; + + default: + if (isalpha(cs1)) { + if (do_lint) + lintwarn(_("ignoring unknown format specifier character `%c': no argument converted"), cs1); + if (do_parse_fmt) + goto out; + } + break; + } + if (toofew) { + msg("%s\n\t`%s'\n\t%*s%s", + _("fatal: not enough arguments to satisfy format string"), + fmt_string, (int) (s1 - fmt_string - 1), "", + _("^ ran out for this one")); + goto out; + } + } + + if (do_lint) { + if (need_format) + lintwarn(_("[s]printf: format specifier does not have control letter")); + if (cur_arg < num_args) + lintwarn(_("too many arguments supplied for format string")); + } + + bchunk(outb, s0, s1 - s0); + retval = bytes2node(outb, NULL); +out: + free_fmt_buf(outb); + + if (do_parse_fmt) { + struct format_spec *cp_spec; + + assert(retval == NULL); + if (spec.fmtchar == (char) 0) + return NULL; + emalloc(cp_spec, struct format_spec *, sizeof (*cp_spec), "format_tree"); + *cp_spec = spec; + return (NODE *) cp_spec; + } + + if (retval == NULL) + gawk_exit(EXIT_FATAL); /* debugger needs this */ + + return retval; + +#undef CP +#undef CEND +#undef CPBUF +} diff --git a/format.h b/format.h new file mode 100644 index 00000000..0c9140dc --- /dev/null +++ b/format.h @@ -0,0 +1,189 @@ +/* + * format.h - (s)printf formatting related definitions. + */ + +/* + * Copyright (C) 2012 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* format specification */ + +struct format_spec { + int base; + long fw; + long prec; + const char *fill; + const char *chbuf; + bool lj; + bool alt; + bool have_prec; + bool zero_flag; + bool quote_flag; + char signchar; + char fmtchar; +}; + +/* indices in `fmt_index' for "%d" and "%.0f" */ + +enum { INT_d_FMT_INDEX, INT_0f_FMT_INDEX }; + + +/* struct to manage awk (s)printf formatted string */ + +struct print_fmt_buf { + char *buf; /* beginning of buffer */ + char *dataend; /* end of current data */ + size_t bufsize; + size_t room_left; + bool is_malloced; /* true if this struct is malloc-ed */ + void (*chksize)(struct print_fmt_buf *, size_t); + void (*cpbuf_chksize)(struct print_fmt_buf *); + + /* + * temporary buffer: can be used to prepend one character at a time + * without overflowing the buffer; used primarily to format integers. + */ + struct { + char *buf; /* beginning of buffer */ + char *bufend; /* end of buffer */ + size_t bufsize; + char *databegin; /* start of current data */ + } cpbuf; +}; + +extern struct print_fmt_buf *get_fmt_buf(void); +extern void format_nondecimal(uintmax_t, struct format_spec *, struct print_fmt_buf *); +extern NODE *format_tree(const char *, size_t, NODE **, long); + +extern char lchbuf[]; +extern char Uchbuf[]; +extern char space_string[]; +extern char zero_string[]; + +# define buf_start(ob) ((ob)->buf) +# define buf_end(ob) ((ob)->dataend) +# define buf_space(ob) ((ob)->room_left) +# define cpbuf_start(ob) ((ob)->cpbuf.databegin) +# define cpbuf_end(ob) ((ob)->cpbuf.bufend) +# define cpbuf(ob) ((ob)->cpbuf.buf) + + +/* chksize --- make room for something LEN big in the output buffer */ + +static inline void +chksize(struct print_fmt_buf *outb, size_t len) +{ + if (len > outb->room_left) + outb->chksize(outb, len); +} + +/* bchunk --- copy LEN bytes from STR checking for space in the process */ + +static inline void +bchunk(struct print_fmt_buf *outb, const char *str, size_t len) +{ + if (len > 0) { + if (len > outb->room_left) + outb->chksize(outb, len); + outb->dataend = (char *) memcpy(outb->dataend, str, len) + len; + outb->room_left -= len; + } +} + +/* bchunk_one --- copy one byte from STR checking for space in the process */ + +static inline void +bchunk_one(struct print_fmt_buf *outb, const char *str) +{ + if (1 > outb->room_left) + outb->chksize(outb, 1); + *outb->dataend++ = *str; + --outb->room_left; +} + +/* buf_adjust --- adjust buffer after appending LEN bytes */ + +static inline void +buf_adjust(struct print_fmt_buf *outb, size_t len) +{ + assert(len <= outb->room_left); + outb->dataend += len; + outb->room_left -= len; +} + +/* bytes2node --- convert bytes to string NODE */ + +static inline NODE * +bytes2node(struct print_fmt_buf *outb, NODE *node) +{ + /* FIXME -- realloc buf? AFAIK, never done before or an issue at all -- JH */ + if (node != NULL) { + node->stptr = outb->buf; + node->stlen = outb->dataend - outb->buf; + } else + node = make_str_node(outb->buf, outb->dataend - outb->buf, ALREADY_MALLOCED); + outb->buf = NULL; + return node; +} + +/* tmpbuf_prepend --- prepend one byte to temporary buffer */ + +static inline void +tmpbuf_prepend(struct print_fmt_buf *outb, char ch) +{ + if (outb->cpbuf.databegin == outb->cpbuf.buf) + outb->cpbuf_chksize(outb); + *--outb->cpbuf.databegin = ch; +} + +/* pr_fill --- fill buffer with current fill character */ + +static inline void +pr_fill(struct format_spec *spec, struct print_fmt_buf *outb) +{ + while (spec->fw > spec->prec) { + bchunk_one(outb, spec->fill); + spec->fw--; + } +} + +static inline void +pr_num_tail(const char *cp, size_t copy_count, struct format_spec *spec, struct print_fmt_buf *outb) +{ + if (! spec->lj) + pr_fill(spec, outb); + bchunk(outb, cp, copy_count); + pr_fill(spec, outb); +} + + +/* free_print_fmt_buf --- free buffer */ + +static inline void +free_fmt_buf(struct print_fmt_buf *outb) +{ + if (outb->buf != NULL) { + efree(outb->buf); + outb->buf = NULL; + } + efree(outb->cpbuf.buf); + if (outb->is_malloced) + efree(outb); +} @@ -636,7 +636,7 @@ api_sym_update_scalar(awk_ext_id_t id, */ switch (value->val_type) { case AWK_NUMBER: - if (node->var_value->valref == 1 && ! do_mpfr) { + if (node->var_value->valref == 1 && numbr_hndlr == & awknum_hndlr) { NODE *r = node->var_value; /* r_unref: */ @@ -660,7 +660,8 @@ api_sym_update_scalar(awk_ext_id_t id, if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) efree(r->stptr); - mpfr_unset(r); + if (free_number && (r->flags & (NUMBER|NUMCUR)) != 0) + free_number(r); free_wstr(r); /* make_str_node(s, l, ALREADY_MALLOCED): */ @@ -1130,7 +1131,7 @@ init_ext_api() api_impl.do_flags[2] = (do_profile ? 1 : 0); api_impl.do_flags[3] = (do_sandbox ? 1 : 0); api_impl.do_flags[4] = (do_debug ? 1 : 0); - api_impl.do_flags[5] = (do_mpfr ? 1 : 0); + api_impl.do_flags[5] = (numbr_hndlr != & awknum_hndlr); } /* update_ext_api --- update the variables in the API that can change */ diff --git a/int_array.c b/int_array.c index c2bf37b5..6caf512e 100644 --- a/int_array.c +++ b/int_array.c @@ -85,7 +85,7 @@ is_integer(NODE *symbol, NODE *subs) long l; AWKNUM d; - if (subs == Nnull_string || do_mpfr) + if (subs == Nnull_string) return NULL; if ((subs->flags & NUMINT) != 0) diff --git a/interpret.h b/interpret.h index 28804330..e84c7b36 100644 --- a/interpret.h +++ b/interpret.h @@ -34,9 +34,9 @@ r_interpret(INSTRUCTION *code) INSTRUCTION *ni; NODE *t1, *t2; NODE **lhs; - AWKNUM x, x2; int di; Regexp *rp; + NODE *booleans[] = { false_node, true_node }; NODE *set_array = NULL; /* array with a post-assignment routine */ NODE *set_idx = NULL; /* the index of the array element */ @@ -186,7 +186,7 @@ uninitialized_scalar: cant_happen(); } } - break; + break; case Op_push_param: /* function argument */ m = pc->memory; @@ -398,7 +398,7 @@ uninitialized_scalar: DEREF(t1); if ((op == Op_and && di) || (op == Op_or && ! di)) break; - r = node_Boolean[di]; + r = booleans[di]; UPREF(r); PUSH(r); ni = pc->target_jmp; @@ -407,7 +407,7 @@ uninitialized_scalar: case Op_and_final: case Op_or_final: t1 = TOP_SCALAR(); - r = node_Boolean[eval_condition(t1)]; + r = booleans[eval_condition(t1)]; DEREF(t1); UPREF(r); REPLACE(r); @@ -415,181 +415,115 @@ uninitialized_scalar: case Op_not: t1 = TOP_SCALAR(); - r = node_Boolean[! eval_condition(t1)]; + r = booleans[! eval_condition(t1)]; DEREF(t1); UPREF(r); REPLACE(r); break; case Op_equal: - r = node_Boolean[cmp_scalars() == 0]; + r = booleans[cmp_scalars() == 0]; UPREF(r); REPLACE(r); break; case Op_notequal: - r = node_Boolean[cmp_scalars() != 0]; + r = booleans[cmp_scalars() != 0]; UPREF(r); REPLACE(r); break; case Op_less: - r = node_Boolean[cmp_scalars() < 0]; + r = booleans[cmp_scalars() < 0]; UPREF(r); REPLACE(r); break; case Op_greater: - r = node_Boolean[cmp_scalars() > 0]; + r = booleans[cmp_scalars() > 0]; UPREF(r); REPLACE(r); break; case Op_leq: - r = node_Boolean[cmp_scalars() <= 0]; + r = booleans[cmp_scalars() <= 0]; UPREF(r); REPLACE(r); break; case Op_geq: - r = node_Boolean[cmp_scalars() >= 0]; + r = booleans[cmp_scalars() >= 0]; UPREF(r); REPLACE(r); break; - case Op_plus_i: - x2 = force_number(pc->memory)->numbr; - goto plus; +#define ARITHOP(OP) \ +t2 = POP_NUMBER(); \ +t1 = TOP_NUMBER(); \ +r = numbr_hndlr->OP(t1, t2); \ +DEREF(t1); \ +DEREF(t2); REPLACE(r) + case Op_plus: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -plus: - t1 = TOP_NUMBER(); - r = make_number(t1->numbr + x2); - DEREF(t1); - REPLACE(r); + ARITHOP(add); break; - case Op_minus_i: - x2 = force_number(pc->memory)->numbr; - goto minus; case Op_minus: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -minus: - t1 = TOP_NUMBER(); - r = make_number(t1->numbr - x2); - DEREF(t1); - REPLACE(r); + ARITHOP(sub); break; - case Op_times_i: - x2 = force_number(pc->memory)->numbr; - goto times; case Op_times: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -times: - t1 = TOP_NUMBER(); - r = make_number(t1->numbr * x2); - DEREF(t1); - REPLACE(r); + ARITHOP(mul); break; - case Op_exp_i: - x2 = force_number(pc->memory)->numbr; - goto exp; - case Op_exp: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -exp: - t1 = TOP_NUMBER(); - r = make_number(calc_exp(t1->numbr, x2)); - DEREF(t1); - REPLACE(r); + case Op_quotient: + ARITHOP(div); break; - case Op_quotient_i: - x2 = force_number(pc->memory)->numbr; - goto quotient; - case Op_quotient: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -quotient: - t1 = TOP_NUMBER(); - if (x2 == 0) - fatal(_("division by zero attempted")); - r = make_number(t1->numbr / x2); - DEREF(t1); - REPLACE(r); - break; + case Op_exp: + ARITHOP(pow); + break; - case Op_mod_i: - x2 = force_number(pc->memory)->numbr; - goto mod; case Op_mod: - t2 = POP_NUMBER(); - x2 = t2->numbr; - DEREF(t2); -mod: - t1 = TOP_NUMBER(); - if (x2 == 0) - fatal(_("division by zero attempted in `%%'")); -#ifdef HAVE_FMOD - x = fmod(t1->numbr, x2); -#else /* ! HAVE_FMOD */ - (void) modf(t1->numbr / x2, &x); - x = t1->numbr - x * x2; -#endif /* ! HAVE_FMOD */ - r = make_number(x); - - DEREF(t1); - REPLACE(r); + ARITHOP(mod); break; +#undef ARITHOP case Op_preincrement: case Op_predecrement: - x = op == Op_preincrement ? 1.0 : -1.0; lhs = TOP_ADDRESS(); - t1 = *lhs; - force_number(t1); - if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { - /* optimization */ - t1->numbr += x; - r = t1; - } else { - r = *lhs = make_number(t1->numbr + x); - unref(t1); - } + t1 = force_number(*lhs); + r = *lhs = numbr_hndlr->add_long(t1, op == Op_preincrement ? 1 : -1); + unref(t1); UPREF(r); REPLACE(r); break; case Op_postincrement: case Op_postdecrement: - x = op == Op_postincrement ? 1.0 : -1.0; lhs = TOP_ADDRESS(); - t1 = *lhs; - force_number(t1); - r = make_number(t1->numbr); - if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { - /* optimization */ - t1->numbr += x; - } else { - *lhs = make_number(t1->numbr + x); - unref(t1); - } + t1 = force_number(*lhs); + r = numbr_hndlr->gawk_copy_number(t1); + *lhs = numbr_hndlr->add_long(t1, op == Op_postincrement ? 1 : -1); + unref(t1); + REPLACE(r); + break; + + case Op_unary_plus: + /* + * POSIX semantics: force a conversion to numeric type. + * Arbitrary-precision semantics: force the working precision. + */ + t1 = TOP_NUMBER(); + r = numbr_hndlr->gawk_copy_number(t1); + DEREF(t1); REPLACE(r); break; case Op_unary_minus: t1 = TOP_NUMBER(); - r = make_number(-t1->numbr); + r = numbr_hndlr->gawk_copy_number(t1); + numbr_hndlr->gawk_negate_number(r); DEREF(t1); REPLACE(r); break; @@ -840,7 +774,7 @@ mod: case Op_in_array: t1 = POP_ARRAY(); t2 = mk_sub(pc->expr_count); - r = node_Boolean[(in_array(t1, t2) != NULL)]; + r = booleans[(in_array(t1, t2) != NULL)]; DEREF(t2); UPREF(r); PUSH(r); @@ -989,13 +923,13 @@ match_re: */ di = research(rp, t1->stptr, 0, t1->stlen, - avoid_dfa(m, t1->stptr, t1->stlen)); + avoid_dfa(m, t1->stptr, t1->stlen)); di = (di == -1) ^ (op != Op_nomatch); if (op != Op_match_rec) { decr_sp(); DEREF(t1); } - r = node_Boolean[di]; + r = booleans[di]; UPREF(r); PUSH(r); break; @@ -1368,8 +1302,8 @@ match_re: } result = ip->triggered || di; - ip->triggered ^= di; /* update triggered flag */ - r = node_Boolean[result]; /* final value of condition pair */ + ip->triggered ^= di; /* update triggered flag */ + r = booleans[result]; /* final value of condition pair */ UPREF(r); REPLACE(r); JUMPTO(pc->target_jmp); @@ -192,14 +192,6 @@ # endif #endif -#ifdef HAVE_MPFR -/* increment NR or FNR */ -#define INCREMENT_REC(X) (do_mpfr && X == (LONG_MAX - 1)) ? \ - (mpz_add_ui(M##X, M##X, 1), X = 0) : X++ -#else -#define INCREMENT_REC(X) X++ -#endif - typedef enum { CLOSE_ALL, CLOSE_TO, CLOSE_FROM } two_way_close_type; /* Several macros to make the code a bit clearer. */ @@ -458,6 +450,7 @@ nextfile(IOBUF **curfile, bool skipping) return 0; } + (void) force_number(ARGC_node->var_value); /* make no assumptions! */ argc = get_number_si(ARGC_node->var_value); for (; i < argc; i++) { @@ -481,11 +474,11 @@ nextfile(IOBUF **curfile, bool skipping) /* manage the awk variables: */ unref(FILENAME_node->var_value); FILENAME_node->var_value = dupnode(arg); -#ifdef HAVE_MPFR - if (is_mpg_number(FNR_node->var_value)) - mpz_set_ui(MFNR, 0); -#endif - FNR = 0; + + unref(FNR_node->var_value); + FNR_node->var_value = dupnode(false_node); + numbr_hndlr->set_numvar(FNR_node); + assert(FNR == 0); /* IOBUF management: */ errno = 0; @@ -547,14 +540,8 @@ nextfile(IOBUF **curfile, bool skipping) void set_FNR() { - NODE *n = FNR_node->var_value; - (void) force_number(n); -#ifdef HAVE_MPFR - if (is_mpg_number(n)) - FNR = mpg_set_var(FNR_node); - else -#endif - FNR = get_number_si(n); + (void) force_number(FNR_node->var_value); + numbr_hndlr->set_numvar(FNR_node); } /* set_NR --- update internal NR from awk variable */ @@ -562,14 +549,8 @@ set_FNR() void set_NR() { - NODE *n = NR_node->var_value; - (void) force_number(n); -#ifdef HAVE_MPFR - if (is_mpg_number(n)) - NR = mpg_set_var(NR_node); - else -#endif - NR = get_number_si(n); + (void) force_number(NR_node->var_value); + numbr_hndlr->set_numvar(NR_node); } /* inrec --- This reads in a record from the input file */ @@ -591,8 +572,17 @@ inrec(IOBUF *iop, int *errcode) if (cnt == EOF) { retval = false; } else { - INCREMENT_REC(NR); - INCREMENT_REC(FNR); +#if 0 + /* XXX: looser if AWKNUM is long double */ + if (numbr_hndlr == & awknum_hndlr) { + NR++; + FNR++; + } else +#endif + { + NR = numbr_hndlr->increment_var(NR_node, NR); + FNR = numbr_hndlr->increment_var(FNR_node, FNR); + } set_record(begin, cnt); if (*errcode > 0) retval = false; @@ -2472,8 +2462,17 @@ do_getline(int into_variable, IOBUF *iop) if (cnt == EOF) return NULL; /* try next file */ - INCREMENT_REC(NR); - INCREMENT_REC(FNR); +#if 0 + /* XXX: looser if AWKNUM is long double */ + if (numbr_hndlr == & awknum_hndlr) { + NR++; + FNR++; + } else +#endif + { + NR = numbr_hndlr->increment_var(NR_node, NR); + FNR = numbr_hndlr->increment_var(FNR_node, FNR); + } if (! into_variable) /* no optional var. */ set_record(s, cnt); @@ -45,8 +45,6 @@ typedef void *stackoverflow_context_t; #define DEFAULT_PROFILE "awkprof.out" /* where to put profile */ #define DEFAULT_VARFILE "awkvars.out" /* where to put vars */ -#define DEFAULT_PREC 53 -#define DEFAULT_ROUNDMODE "N" /* round to nearest */ static const char *varfile = DEFAULT_VARFILE; const char *command_file = NULL; /* debugger commands */ @@ -68,9 +66,12 @@ static void version(void) ATTRIBUTE_NORETURN; static void init_fds(void); static void init_groupset(void); static void save_argv(int, char **); +static void init_numbr_handler(bltin_t **); +static void print_numbr_hndlr_versions(void); extern int debug_prog(INSTRUCTION *pc); /* debug.c */ -extern int init_debug(); /* debug.c */ +extern int init_debug(void); /* debug.c */ +extern void init_parser(const bltin_t *); /* awkgram.c */ /* These nodes store all the special variables AWK uses */ NODE *ARGC_node, *ARGIND_node, *ARGV_node, *BINMODE_node, *CONVFMT_node; @@ -121,6 +122,8 @@ INSTRUCTION *rule_list; int exit_val = EXIT_SUCCESS; /* exit value */ +numbr_handler_t *numbr_hndlr; /* number handler */ + #if defined(YYDEBUG) || defined(GAWKDEBUG) extern int yydebug; #endif @@ -221,6 +224,10 @@ main(int argc, char **argv) char *extra_stack; int have_srcfile = 0; SRCFILE *s; + bltin_t *numbr_bltins; + + /* default number handler */ + numbr_hndlr = & awknum_hndlr; /* do these checks early */ if (getenv("TIDYMEM") != NULL) @@ -308,7 +315,7 @@ main(int argc, char **argv) #undef STACK_SIZE myname = gawk_name(argv[0]); - os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */ + os_arg_fixup(& argc, & argv); /* emulate redirection, expand wildcards */ if (argc < 2) usage(EXIT_FAILURE, stderr); @@ -319,12 +326,6 @@ main(int argc, char **argv) /* Robustness: check that file descriptors 0, 1, 2 are open */ init_fds(); - /* init array handling. */ - array_init(); - - /* init the symbol tables */ - init_symbol_table(); - output_fp = stdout; /* we do error messages ourselves on invalid options */ @@ -470,7 +471,7 @@ main(int argc, char **argv) case 'M': #ifdef HAVE_MPFR - do_flags |= DO_MPFR; + numbr_hndlr = & mpfp_hndlr; #else warning(_("-M ignored: MPFR/GMP support not compiled in")); #endif @@ -595,29 +596,21 @@ out: } #endif + /* Set up number handler */ + init_numbr_handler(& numbr_bltins); + if (do_debug) /* Need to register the debugger pre-exec hook before any other */ init_debug(); -#ifdef HAVE_MPFR - /* Set up MPFR defaults, and register pre-exec hook to process arithmetic opcodes */ - if (do_mpfr) - init_mpfr(DEFAULT_PREC, DEFAULT_ROUNDMODE); -#endif + /* init array handling. */ + array_init(); + + /* init the symbol tables */ + init_symbol_table(); /* load group set */ init_groupset(); -#ifdef HAVE_MPFR - if (do_mpfr) { - mpz_init(Nnull_string->mpg_i); - Nnull_string->flags = (MALLOC|STRCUR|STRING|MPZN|NUMCUR|NUMBER); - } else -#endif - { - Nnull_string->numbr = 0.0; - Nnull_string->flags = (MALLOC|STRCUR|STRING|NUMCUR|NUMBER); - } - /* * Tell the regex routines how they should work. * Do this before initializing variables, since @@ -682,7 +675,7 @@ out: optind++; } - /* Select the interpreter routine */ + /* select the interpreter routine */ init_interpret(); init_args(optind, argc, @@ -697,6 +690,10 @@ out: */ setlocale(LC_NUMERIC, "C"); #endif + + /* initialize parser related variables */ + init_parser(numbr_bltins); + /* Read in the program */ if (parse_program(& code_block) != 0) exit(EXIT_FAILURE); @@ -755,11 +752,6 @@ out: if (do_dump_vars) dump_vars(varfile); -#ifdef HAVE_MPFR - if (do_mpfr) - cleanup_mpfr(); -#endif - if (do_tidy_mem) release_all_vars(); @@ -1011,7 +1003,7 @@ static const struct varinit varinit[] = { {&FPAT_node, "FPAT", "[^[:space:]]+", 0, NULL, set_FPAT, false, NON_STANDARD }, {&IGNORECASE_node, "IGNORECASE", NULL, 0, NULL, set_IGNORECASE, false, NON_STANDARD }, {&LINT_node, "LINT", NULL, 0, NULL, set_LINT, false, NON_STANDARD }, -{&PREC_node, "PREC", NULL, DEFAULT_PREC, NULL, set_PREC, false, NON_STANDARD}, +{&PREC_node, "PREC", NULL, 0, NULL, set_PREC, false, NON_STANDARD}, {&NF_node, "NF", NULL, -1, update_NF, set_NF, false, 0 }, {&NR_node, "NR", NULL, 0, update_NR, set_NR, true, 0 }, {&OFMT_node, "OFMT", "%.6g", 0, NULL, set_OFMT, true, 0 }, @@ -1019,7 +1011,7 @@ static const struct varinit varinit[] = { {&ORS_node, "ORS", "\n", 0, NULL, set_ORS, true, 0 }, {NULL, "PROCINFO", NULL, 0, NULL, NULL, false, NO_INSTALL | NON_STANDARD | NOT_OFF_LIMITS }, {&RLENGTH_node, "RLENGTH", NULL, 0, NULL, NULL, false, 0 }, -{&ROUNDMODE_node, "ROUNDMODE", DEFAULT_ROUNDMODE, 0, NULL, set_ROUNDMODE, false, NON_STANDARD }, +{&ROUNDMODE_node, "ROUNDMODE", "", 0, NULL, set_ROUNDMODE, false, NON_STANDARD }, {&RS_node, "RS", "\n", 0, NULL, set_RS, true, 0 }, {&RSTART_node, "RSTART", NULL, 0, NULL, NULL, false, 0 }, {&RT_node, "RT", "", 0, NULL, NULL, false, NON_STANDARD }, @@ -1050,6 +1042,8 @@ init_vars() (*(vp->assign))(); } + numbr_hndlr->init_numvars(); /* set default values for variables e.g. PREC */ + /* Set up deferred variables (loaded only when accessed). */ if (! do_traditional) register_deferred_variable("PROCINFO", load_procinfo); @@ -1142,7 +1136,7 @@ load_procinfo() #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0 int i; #endif -#if (defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0) || defined(HAVE_MPFR) +#if (defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0) char name[100]; #endif AWKNUM value; @@ -1158,14 +1152,8 @@ load_procinfo() update_PROCINFO_str("version", VERSION); update_PROCINFO_str("strftime", def_strftime_format); -#ifdef HAVE_MPFR - sprintf(name, "GNU MPFR %s", mpfr_get_version()); - update_PROCINFO_str("mpfr_version", name); - sprintf(name, "GNU MP %s", gmp_version); - update_PROCINFO_str("gmp_version", name); - update_PROCINFO_num("prec_max", MPFR_PREC_MAX); - update_PROCINFO_num("prec_min", MPFR_PREC_MIN); -#endif + if (numbr_hndlr != & awknum_hndlr && numbr_hndlr->load_procinfo) + numbr_hndlr->load_procinfo(); #ifdef DYNAMIC update_PROCINFO_num("api_major", GAWK_API_MAJOR_VERSION); @@ -1469,9 +1457,7 @@ version() #ifdef DYNAMIC printf(", API: %d.%d", GAWK_API_MAJOR_VERSION, GAWK_API_MINOR_VERSION); #endif -#ifdef HAVE_MPFR - printf(" (GNU MPFR %s, GNU MP %s)", mpfr_get_version(), gmp_version); -#endif + print_numbr_hndlr_versions(); printf("\n"); print_ext_versions(); @@ -1588,6 +1574,44 @@ init_locale(struct lconv *l) } #endif /* LOCALE_H */ +static void +init_numbr_handler(bltin_t **bltins) +{ + if (! numbr_hndlr->init(bltins)) { + /* not available */ + numbr_hndlr = & awknum_hndlr; /* fall back to AWKNUM */ + (void) numbr_hndlr->init(bltins); + } + + make_number = numbr_hndlr->gawk_make_number; + str2number = numbr_hndlr->gawk_force_number; + format_val = numbr_hndlr->gawk_fmt_number; + cmp_numbers = numbr_hndlr->gawk_cmp_numbers; + str2node = numbr_hndlr->gawk_str2number; + free_number = numbr_hndlr->gawk_free_number; + format_number_printf = numbr_hndlr->gawk_format_printf; + get_number_d = numbr_hndlr->gawk_todouble; + get_number_si = numbr_hndlr->gawk_tolong; + get_number_ui = numbr_hndlr->gawk_toulong; + get_number_uj = numbr_hndlr->gawk_touintmax_t; + sgn_number = numbr_hndlr->gawk_sgn_number; +} + +static void +print_numbr_hndlr_versions() +{ + static numbr_handler_t *hndlrs[] = { + & awknum_hndlr, + & mpfp_hndlr, + }; + int i; + + for (i = 0; i < sizeof(hndlrs) / sizeof(hndlrs[0]); i++) + if (hndlrs[i]->version_str) + printf(" (%s)", hndlrs[i]->version_str()); +} + + /* save_argv --- save argv array */ static void diff --git a/misc/ap_math.awk b/misc/ap_math.awk new file mode 100644 index 00000000..941a4d61 --- /dev/null +++ b/misc/ap_math.awk @@ -0,0 +1,331 @@ +# ap_math.awk --- arbitrary-precision math functions + +# +# ap_sin -- Compute sin(x) +# ap_cos -- Compute cos(x) +# ap_atan2 -- Compute atan2(y, x) +# + +# +# Machin's formula to compute pi (http://mathworld.wolfram.com/MachinsFormula.html): +# pi / 4 = 4 acot(5) - acot(239) +# = 4 atan(1/5) - atan(1/239) +# +# Euler atan formula (http://mathworld.wolfram.com/InverseTangent.html): +# atan(x) = (y/x) (1 + 2/3 y + (2·4)/(3·5) y^2 + (2·4·6)/(3·5·7) y^3 +...) +# where +# y = (x^2) / (1 + x^2) and -1 <= x <= 1 +# +# Substituting x = 1/x, for x >= 1 +# atan(1/x) = (x / (1 + x^2)) + (2/3) * (x / (1 + x^2)^2) +# + (2*4/(3*5)) * (x / (1 + x^2)^3) +# + (2*4*6/(3*5*7)) * (x / (1 + x^2)^4) + ... +# + +function euler_atan_one_over(x, \ + xpow2_plus_one, term, sum, i, sign, err) +{ + sign = 1 + if (x < 0) { + sign = -1 + x = -x + } + + xpow2_plus_one = x * x + 1 + term = x / xpow2_plus_one + sum = term + i = 0 + + do { + term *= (i + 2) / (i + 3) + err = term /= xpow2_plus_one + i += 2 + sum += term + err = term / sum + } while (err > __REL_ERROR__) + + return sign * sum +} + +function setup_ap_math( \ + prec, curr_prec, digits, extra_prec) +{ + switch (PREC) { + case "half": prec = 11; break; + case "single": prec = 24; break; + case "double": prec = 53; break; + case "quad": prec = 113; break; + case "oct": prec = 237; break; + default: prec = PREC + 0; + } + + if (prec <= 0) { + # double or long double ? + print "PREC value not specified" > "/dev/stderr" + exit(1) + } + + curr_prec = PREC + extra_prec = 10 # temporarily raise precision by this amount; Why 10??? + + # unlike PREC, `prec' is always a number + PREC = prec + extra_prec + + if (PREC != __PI_PREC__) { + # compute PI only once for a given precision + digits = int (PREC / 3.32193) + __REL_ERROR__ = sprintf("5.0e-%d", digits) + 0 + __PI_PREC__ = PREC + __PI_OVER_4__ = 4 * euler_atan_one_over(5) - euler_atan_one_over(239) + } + return curr_prec +} + +# +# atan2(y, x) = atan(y/x), x > 0 +# = atan(y/x) + pi, x < 0, y >= 0 +# = atan(y/x) - pi, x < 0, y < 0 +# = pi/2, x = 0, y > 0 +# = -pi/2, x = 0, y < 0 +# = ? x = 0, y = 0 +# + +function euler_atan2(y, x, \ + sign, plus_inf, minus_inf) +{ + # Using Euler atan(1/x) and the identity: + # atan(x) = - pi / 2 - atan(1/x), x < 0 + # = pi / 2 - atan(1/x), x > 0 + + plus_inf = "+inf" + 0 + minus_inf = "-inf" + 0 + + # detect all the "abnormal" cases first + x = + x # or x += 0.0 or x = x + 0.0 + y = + y + if (x == "+nan" + 0 || x == "-nan" + 0 || + y == "+nan" + 0 || y == "-nan" + 0) + return "nan" + 0 + + if (y == plus_inf) { + if (x == minus_inf) + return 3 * __PI_OVER_4__ + else if (x == plus_inf) + return __PI_OVER_4__ + else + return 2 * __PI_OVER_4__ + } else if (y == minus_inf) { + if (x == minus_inf) + return - 3 * __PI_OVER_4__ + else if (x == plus_inf) + return - __PI_OVER_4__ + else + return - 2 * __PI_OVER_4__ + } + + if (x == plus_inf) + return atan2(y, x) # use builtin, returns +0 or -0 + if (x == minus_inf) { + if (y >= 0) + return 4 * __PI_OVER_4__ + # y < 0 + return - 4 * __PI_OVER_4__ + } + + if (x > 0) { + if (y == 0) + return atan2(y, x); # use builtin; returns +0 or -0 + sign = 1 + if (y < 0) { + sign = -1 + y = -y + } + if (y > x) + return sign * (2 * __PI_OVER_4__ - euler_atan_one_over(y / x)) + return sign * euler_atan_one_over(x / y) + } + + if (x < 0) { + if (y == 0) { + if (atan2(y, x) < 0) # use builtin to detect sign + return - 4 * __PI_OVER_4__ + return 4 * __PI_OVER_4__ + } + + if (y < 0) { + y = -y + x = -x + if (y > x) + return - 2 * __PI_OVER_4__ - euler_atan_one_over(y / x) + return euler_atan_one_over(x / y) - 4 * __PI_OVER_4__ + } + # y > 0 + x = -x + if (y > x) + return 2 * __PI_OVER_4__ + euler_atan_one_over(y / x) + return - euler_atan_one_over(x / y) + 4 * __PI_OVER_4__ + } + + if (atan2(y, x) < 0) # atan2(-0, -0) + return - 4.0 * __PI_OVER_4__ + if (atan2(y, x) > 0) # atan2(+0, -0) + return 4.0 * __PI_OVER_4__ + return 0; # atan2(+0, +0) or atan2(-0, 0) +} + +# +# Collect two terms in each iteration for the Taylor series: +# sin(x) = (x - x^3/3!) + (x^5/5! - x^7/7!) + ... +# + +function taylor_sin(x, \ + i, fact, xpow2, xpow_odd, sum, term, err) +{ + # XXX: this assumes x >= 0 + + if (x == 0) + return x + i = 3 + fact = 6 # 3! + xpow2 = x * x + xpow_odd = xpow2 * x + sum = x - xpow_odd / fact + + do { + fact *= (i + 1) * (i + 2) + i += 2 + xpow_odd *= xpow2 + term = xpow_odd / fact + + fact *= (i + 1) * (i + 2) + i += 2 + xpow_odd *= xpow2 + term -= xpow_odd / fact + + sum += term + err = term / sum + } while (err > __REL_ERROR__) + + return sum +} + +# +# Collect two terms in each iteration for the Taylor series: +# cos(x) = (1 - x^2/2!) + (x^4/4! - x^6/6!)... +# + +function taylor_cos(x, \ + i, fact, xpow2, xpow_even, sum, term, err) +{ + if (x == 0) + return 1 + + i = 2 + fact = 2 + xpow2 = x * x + xpow_even = xpow2 + sum = 1 - xpow2 / fact + + do { + fact *= (i + 1) * (i + 2) + i += 2 + xpow_even *= xpow2 + term = xpow_even / fact + + fact *= (i + 1) * (i + 2) + i += 2 + xpow_even *= xpow2 + term -= xpow_even / fact + + sum += term + err = term / sum + } while (err > __REL_ERROR__) + + return sum +} + +# +# For 0 <= x <= PI/4, using Taylor series approximation for sin(x): +# x - x^3/3! + x^5/5! - ... +# +# for PI/4 < x <= PI/2, use identity sin(x) = cos(PI/2 - x). +# +# + +function ap_sin(x, \ + k, sign, y, sv, curr_prec) +{ + curr_prec = setup_ap_math() + x = + x # or x += 0.0 or x = x + 0.0 + if (x == "+inf" + 0 || x == "-inf" + 0 || + x == "+nan" + 0 || x == "-nan" + 0) + return "nan" + 0 + + if (x < 0) { + # sin(-x) = - sin(x) + sign = -1 + x = -x + } else + sign = 1 + + # range reduction -- 0 <= y <= pi / 4 + + k = int(x / __PI_OVER_4__) + sign *= ( int(k / 4) % 2 ? -1 : 1 ) + switch (k % 4) { + case 0: y = x - k * __PI_OVER_4__; sv = taylor_sin(y); break; + case 1: y = (k + 1) * __PI_OVER_4__ - x; sv = taylor_cos(y); break; + case 2: y = x - k * __PI_OVER_4__; sv = taylor_cos(y); break; + case 3: y = (k + 1) * __PI_OVER_4__ - x; sv = taylor_sin(y); break; + } + sv *= sign + + PREC = curr_prec + return +sv; # unary plus returns a number with current precision +} + +# +# Using Taylor series approximation for sin(x) for 0 <= x <= PI/4: +# 1 - x^2/2! + x^4/4! - ... +# for PI/4 < x < PI/2, use identity cos(x) = sin(PI/2 - x). +# + + +function ap_cos(x, \ + k, sign, y, cv, curr_prec) +{ + curr_prec = setup_ap_math() + + x = + x # or x += 0.0 or x = x + 0.0 + if (x == "+inf" + 0 || x == "-inf" + 0 || + x == "+nan" + 0 || x == "-nan" + 0) + return "nan" + 0 + + if (x < 0) # cos(x) = cos(-x) + x = -x + + # range reduction -- 0 <= y <= pi / 4 + + k = int(x / __PI_OVER_4__) + sign = ( int(k / 4) % 2 ? -1 : 1 ) + switch (k % 4) { + case 0: y = x - k * __PI_OVER_4__; cv = taylor_cos(y); break; + case 1: y = (k + 1) * __PI_OVER_4__ - x; cv = taylor_sin(y); break; + case 2: y = x - k * __PI_OVER_4__; cv = -taylor_sin(y); break; + case 3: y = (k + 1) * __PI_OVER_4__ - x; cv = -taylor_cos(y); break; + } + cv *= sign + + PREC = curr_prec + return +cv # unary plus to apply current precision +} + +function ap_atan2(y, x, \ + tv, curr_prec) +{ + curr_prec = setup_ap_math() + tv = euler_atan2(y, x) + + PREC = curr_prec + return +tv # unary plus to apply current precision +} @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2012, 2013 the Free Software Foundation, Inc. + * Copyright (C) 2012 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Programming Language. @@ -26,99 +26,331 @@ #include "awk.h" #ifdef HAVE_MPFR +#include <gmp.h> +#include <mpfr.h> + +#include "format.h" + +#ifndef MPFR_RNDN +/* for compatibility with MPFR 2.X */ +#define MPFR_RNDN GMP_RNDN +#define MPFR_RNDZ GMP_RNDZ +#define MPFR_RNDU GMP_RNDU +#define MPFR_RNDD GMP_RNDD +#endif #if !defined(MPFR_VERSION_MAJOR) || MPFR_VERSION_MAJOR < 3 typedef mp_exp_t mpfr_exp_t; #endif -extern NODE **fmt_list; /* declared in eval.c */ - -mpz_t mpzval; /* GMP integer type, used as temporary in few places */ -mpz_t MNR; -mpz_t MFNR; -bool do_ieee_fmt; /* IEEE-754 floating-point emulation */ -mpfr_rnd_t ROUND_MODE; - -static mpfr_rnd_t get_rnd_mode(const char rmode); -static NODE *mpg_force_number(NODE *n); -static NODE *mpg_make_number(double); -static NODE *mpg_format_val(const char *format, int index, NODE *s); -static int mpg_interpret(INSTRUCTION **cp); +#define DEFAULT_PREC 53 +#define DEFAULT_ROUNDMODE "N" /* round to nearest */ + +/* exported functions */ +static NODE *mpfp_make_number(AWKNUM); +static int mpfp_compare(const NODE *, const NODE *); +static void mpfp_negate_num(NODE *); +static NODE *mpfp_str2node(char *, char **, int, bool); +static NODE *mpfp_force_number(NODE *); +static void mpfp_free_num(NODE *); +static NODE *mpfp_format_val(const char *, int, NODE *); +static unsigned long mpfp_toulong(const NODE *); +static long mpfp_tolong(const NODE *); +static double mpfp_todouble(const NODE *); +static uintmax_t mpfp_touintmax_t(const NODE *); +static int mpfp_sgn(const NODE *); +static bool mpfp_isinteger(const NODE *); +static bool mpfp_isnan(const NODE *); +static bool mpfp_isinf(const NODE *); +static NODE *mpfp_copy_number(const NODE *); +static int mpfp_format_printf(NODE *, struct format_spec *, struct print_fmt_buf *); +static bool mpfp_init(bltin_t **); +static NODE *mpfp_add(const NODE *, const NODE *); +static NODE *mpfp_sub(const NODE *, const NODE *); +static NODE *mpfp_mul(const NODE *, const NODE *); +static NODE *mpfp_div(const NODE *, const NODE *); +static NODE *mpfp_mod(const NODE *, const NODE *); +static NODE *mpfp_pow(const NODE *, const NODE *); +static NODE *mpfp_add_long(const NODE *, long); +static NODE *mpfp_update_var(NODE *); +static void mpfp_set_var(const NODE *); +static long mpfp_increment_var(const NODE *, long); +static void mpfp_init_vars(void); +static void mpfp_load_procinfo(void); +static const char *mpfp_version_string(void); + +/* builtins */ +static NODE *do_mpfp_and(int); +static NODE *do_mpfp_atan2(int); +static NODE *do_mpfp_compl(int); +static NODE *do_mpfp_cos(int); +static NODE *do_mpfp_div(int); +static NODE *do_mpfp_exp(int); +static NODE *do_mpfp_int(int); +static NODE *do_mpfp_log(int); +static NODE *do_mpfp_lshift(int); +static NODE *do_mpfp_or(int); +static NODE *do_mpfp_rand(int); +static NODE *do_mpfp_rshift(int); +static NODE *do_mpfp_sin(int); +static NODE *do_mpfp_sqrt(int); +static NODE *do_mpfp_srand(int); +static NODE *do_mpfp_strtonum(int); +static NODE *do_mpfp_xor(int); + +/* internal functions */ +static NODE *mpfp_make_node(unsigned int type); +static int mpfp_format_ieee(mpfr_ptr, int); +static const char *mpfp_sprintf(const char *, ...); +static int mpfp_strtoui(mpz_ptr, char *, size_t, char **, int); +static mpfr_rnd_t mpfp_get_rounding_mode(const char rmode); +static mpfr_ptr mpz2mpfr(mpz_ptr mpz_val, mpfr_ptr mpfr_val); + +static mpfr_rnd_t ROUND_MODE; +static mpz_t MNR; +static mpz_t MFNR; +static bool do_ieee_fmt; /* emulate IEEE 754 floating-point format */ static mpfr_exp_t min_exp = MPFR_EMIN_DEFAULT; static mpfr_exp_t max_exp = MPFR_EMAX_DEFAULT; +/* needed for MPFR and GMP macros */ +#define MPFR_T(x) ((mpfr_ptr) x) +#define MPZ_T(x) ((mpz_ptr) x) + + /* temporary MPFR floats used to hold converted GMP integer operands */ -static mpfr_t _mpf_t1; -static mpfr_t _mpf_t2; +static mpfr_t _mp1; +static mpfr_t _mp2; /* - * PRECISION_MIN is the precision used to initialize _mpf_t1 and _mpf_t2. + * PRECISION_MIN is the precision used to initialize _mp1 and _mp2. * 64 bits should be enough for exact conversion of most integers to floats. */ #define PRECISION_MIN 64 -/* mf = { _mpf_t1, _mpf_t2 } */ -static inline mpfr_ptr mpg_tofloat(mpfr_ptr mf, mpz_ptr mz); -/* T = {t1, t2} */ -#define MP_FLOAT(T) is_mpg_integer(T) ? mpg_tofloat(_mpf_##T, (T)->mpg_i) : (T)->mpg_numbr +static mpz_t _mpzval; /* GMP type for float to int conversion in format_tree() */ +static mpfr_t _mpfrval; /* MPFR type for int to float conversion in format_tree() */ + +#define IEEE_FMT(r, t) (void) (do_ieee_fmt && mpfp_format_ieee(r, t)) + +#define mpfp_float() mpfp_make_node(MPFN) +#define mpfp_integer() mpfp_make_node(MPZN) +#define is_mpfp_float(n) (((n)->flags & MPFN) != 0) +#define is_mpfp_integer(n) (((n)->flags & MPZN) != 0) +#define is_mpfp_number(n) (((n)->flags & (MPZN|MPFN)) != 0) -/* init_mpfr --- set up MPFR related variables */ +/* mpfp_tofloat --- convert GMP integer to MPFR float without loosing any precision */ + +static inline mpfr_ptr +mpfp_tofloat(const NODE *t, mpfr_ptr pf) +{ + return is_mpfp_float(t) ? t->qnumbr : mpz2mpfr(t->qnumbr, pf); +} + -void -init_mpfr(mpfr_prec_t prec, const char *rmode) +numbr_handler_t mpfp_hndlr = { + mpfp_init, + mpfp_version_string, + mpfp_load_procinfo, + mpfp_make_number, + mpfp_str2node, + mpfp_copy_number, + mpfp_free_num, + mpfp_force_number, + mpfp_negate_num, + mpfp_compare, + mpfp_sgn, + mpfp_isinteger, + mpfp_isnan, + mpfp_isinf, + mpfp_format_val, + mpfp_format_printf, + mpfp_todouble, + mpfp_tolong, + mpfp_toulong, + mpfp_touintmax_t, + mpfp_add, + mpfp_sub, + mpfp_mul, + mpfp_div, + mpfp_mod, + mpfp_pow, + mpfp_add_long, + mpfp_update_var, + mpfp_set_var, + mpfp_increment_var, + mpfp_init_vars, +}; + + +/* mpfp_init --- set up MPFR related variables */ + +static bool +mpfp_init(bltin_t **numbr_bltins) { - mpfr_set_default_prec(prec); - ROUND_MODE = get_rnd_mode(rmode[0]); + static bltin_t mpfp_bltins[] = { + { "and", do_mpfp_and }, + { "atan2", do_mpfp_atan2 }, + { "compl", do_mpfp_compl }, + { "cos", do_mpfp_cos }, + { "div", do_mpfp_div }, + { "exp", do_mpfp_exp }, + { "int", do_mpfp_int }, + { "log", do_mpfp_log }, + { "lshift", do_mpfp_lshift }, + { "or", do_mpfp_or }, + { "rand", do_mpfp_rand }, + { "rshift", do_mpfp_rshift }, + { "sin", do_mpfp_sin }, + { "sqrt", do_mpfp_sqrt }, + { "srand", do_mpfp_srand }, + { "strtonum", do_mpfp_strtonum }, + { "xor", do_mpfp_xor }, + { NULL, NULL }, + }; + const char *rndmode = DEFAULT_ROUNDMODE; + + mpfr_set_default_prec(DEFAULT_PREC); + ROUND_MODE = mpfp_get_rounding_mode(rndmode[0]); mpfr_set_default_rounding_mode(ROUND_MODE); - make_number = mpg_make_number; - str2number = mpg_force_number; - format_val = mpg_format_val; - cmp_numbers = mpg_cmp; mpz_init(MNR); mpz_init(MFNR); do_ieee_fmt = false; - mpfr_init2(_mpf_t1, PRECISION_MIN); - mpfr_init2(_mpf_t2, PRECISION_MIN); - mpz_init(mpzval); + mpfr_init2(_mp1, PRECISION_MIN); + mpfr_init2(_mp2, PRECISION_MIN); + mpz_init(_mpzval); + mpfr_init2(_mpfrval, PRECISION_MIN); + + /* set the numeric value of null string */ + emalloc(Nnull_string->qnumbr, void *, sizeof (mpz_t), "mpfp_init"); + mpz_init(Nnull_string->qnumbr); /* initialized to 0 */ + Nnull_string->flags |= (MPZN|NUMCUR|NUMBER); + + /* initialize TRUE and FALSE nodes */ + false_node = mpfp_integer(); + true_node = mpfp_integer(); + mpz_set_si(true_node->qnumbr, 1); + + *numbr_bltins = mpfp_bltins; + return true; +} + +static void +mpfp_load_procinfo() +{ + char name[64]; + + snprintf(name, 64, "GNU MPFR %s", mpfr_get_version()); + update_PROCINFO_str("mpfr_version", name); + snprintf(name, 64, "GNU MP %s", gmp_version); + update_PROCINFO_str("gmp_version", name); + update_PROCINFO_num("prec_max", MPFR_PREC_MAX); + update_PROCINFO_num("prec_min", MPFR_PREC_MIN); +} + +static const char * +mpfp_version_string() +{ + static char version_string[64]; + snprintf(version_string, 64, "GNU MPFR %s, GNU MP %s", mpfr_get_version(), gmp_version); + return version_string; +} + +/* mpfp_toulong --- conversion to unsigned long */ + +static unsigned long +mpfp_toulong(const NODE *n) +{ + return (n->flags & MPFN) != 0 ? mpfr_get_ui(n->qnumbr, ROUND_MODE) : mpz_get_ui(n->qnumbr); +} + +/* mpfp_tolong --- conversion to long */ + +static long +mpfp_tolong(const NODE *n) +{ + return (n->flags & MPFN) != 0 ? mpfr_get_si(n->qnumbr, ROUND_MODE) : mpz_get_si(n->qnumbr); +} + +/* mpfp_todouble --- conversion to AWKNUM */ + +static double +mpfp_todouble(const NODE *n) +{ + return (n->flags & MPFN) != 0 ? mpfr_get_d(n->qnumbr, ROUND_MODE) : mpz_get_d(n->qnumbr); +} + +/* mpfp_touintmax_t --- conversion to uintmax_t */ + +static uintmax_t +mpfp_touintmax_t(const NODE *n) +{ + return (n->flags & MPFN) != 0 ? mpfr_get_uj(n->qnumbr, ROUND_MODE) \ + : (uintmax_t) mpz_get_d(n->qnumbr); +} + +/* mpfp_sgn --- return 1 if number > 0, zero if number == 0, and -1 if number < 0 */ + +static int +mpfp_sgn(const NODE *n) +{ + return (n->flags & MPFN) != 0 ? mpfr_sgn(MPFR_T(n->qnumbr)) \ + : mpz_sgn(MPZ_T(n->qnumbr)); +} + +/* mpfp_isinteger --- check if a number is an integer */ + +static bool +mpfp_isinteger(const NODE *n) +{ + return is_mpfp_integer(n) ? true : mpfr_integer_p(n->qnumbr); +} + +/* mpfp_isnan --- check if a number is NaN */ - register_exec_hook(mpg_interpret, 0); +static bool +mpfp_isnan(const NODE *n) +{ + return is_mpfp_float(n) && mpfr_nan_p(MPFR_T(n->qnumbr)); } -/* cleanup_mpfr --- clean stuff up, mainly for valgrind */ +/* mpfp_isinf --- check if a number is infinity */ -void -cleanup_mpfr(void) +static bool +mpfp_isinf(const NODE *n) { - mpfr_clear(_mpf_t1); - mpfr_clear(_mpf_t2); + return is_mpfp_float(n) && mpfr_inf_p(MPFR_T(n->qnumbr)); } -/* mpg_node --- allocate a node to store MPFR float or GMP integer */ -NODE * -mpg_node(unsigned int tp) +/* mpfp_make_node --- allocate a node to store MPFR float or GMP integer */ + +static NODE * +mpfp_make_node(unsigned int type) { NODE *r; + getnode(r); r->type = Node_val; - - if (tp == MPFN) { + if (type == MPFN) { /* Initialize, set precision to the default precision, and value to NaN */ - mpfr_init(r->mpg_numbr); + emalloc(r->qnumbr, void *, sizeof (mpfr_t), "mpfp_make_node"); + mpfr_init(r->qnumbr); r->flags = MPFN; } else { /* Initialize and set value to 0 */ - mpz_init(r->mpg_i); + emalloc(r->qnumbr, void *, sizeof (mpz_t), "mpfp_make_node"); + mpz_init(r->qnumbr); r->flags = MPZN; } r->valref = 1; - r->flags |= MALLOC|NUMBER|NUMCUR; + r->flags |= (MALLOC|NUMBER|NUMCUR); r->stptr = NULL; r->stlen = 0; #if MBS_SUPPORT @@ -129,32 +361,26 @@ mpg_node(unsigned int tp) } /* - * mpg_make_number --- make a arbitrary-precision number node - * and initialize with a C double + * mpfp_make_number --- make a arbitrary-precision number node + * and initialize with AWKNUM. */ static NODE * -mpg_make_number(double x) +mpfp_make_number(AWKNUM x) { NODE *r; - double ival; + int tval; - if ((ival = double_to_int(x)) != x) { - int tval; - r = mpg_float(); - tval = mpfr_set_d(r->mpg_numbr, x, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - } else { - r = mpg_integer(); - mpz_set_d(r->mpg_i, ival); - } + r = mpfp_float(); + tval = mpfr_set_d(r->qnumbr, x, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); return r; } -/* mpg_strtoui --- assign arbitrary-precision integral value from a string */ +/* mpfp_strtoui --- assign arbitrary-precision integral value from a string */ -int -mpg_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base) +static int +mpfp_strtoui(mpz_ptr zi, char *str, size_t len, char **end, int base) { char *s = str; char *start; @@ -219,10 +445,10 @@ done: } -/* mpg_maybe_float --- test if a string may contain arbitrary-precision float */ +/* mpfp_maybe_float --- test if a string may contain arbitrary-precision float */ static int -mpg_maybe_float(const char *str, int use_locale) +mpfp_maybe_float(const char *str, int use_locale) { int dec_point = '.'; const char *s = str; @@ -254,34 +480,40 @@ mpg_maybe_float(const char *str, int use_locale) } -/* mpg_zero --- initialize with arbitrary-precision integer(GMP) and set value to zero */ +/* + * mpfp_init_zero --- initialize with arbitrary-precision integer and set value to zero. + * N.B. : this function also converts MPFR number to GMP number. + */ -static inline void -mpg_zero(NODE *n) +static void +mpfp_init_zero(NODE *n) { - if (is_mpg_float(n)) { - mpfr_clear(n->mpg_numbr); + if (is_mpfp_float(n)) { + mpfr_clear(n->qnumbr); + efree(n->qnumbr); + n->qnumbr = NULL; n->flags &= ~MPFN; } - if (! is_mpg_integer(n)) { - mpz_init(n->mpg_i); /* this also sets its value to 0 */ + if (! is_mpfp_integer(n)) { + emalloc(n->qnumbr, void *, sizeof (mpz_t), "mpfp_init_zero"); + mpz_init(n->qnumbr); /* this also sets its value to 0 */ n->flags |= MPZN; } else - mpz_set_si(n->mpg_i, 0); + mpz_set_si(n->qnumbr, 0); } -/* force_mpnum --- force a value to be a GMP integer or MPFR float */ +/* mpfp_str2num --- force a value to be a GMP integer or MPFR float */ -static int -force_mpnum(NODE *n, int do_nondec, int use_locale) +static bool +mpfp_str2num(NODE *n, int do_nondec, int use_locale) { char *cp, *cpend, *ptr, *cp1; char save; int tval, base = 10; if (n->stlen == 0) { - mpg_zero(n); + mpfp_init_zero(n); /* GMP integer */ return false; } @@ -290,7 +522,7 @@ force_mpnum(NODE *n, int do_nondec, int use_locale) while (cp < cpend && isspace((unsigned char) *cp)) cp++; if (cp == cpend) { /* only spaces */ - mpg_zero(n); + mpfp_init_zero(n); return false; } @@ -305,28 +537,30 @@ force_mpnum(NODE *n, int do_nondec, int use_locale) if (do_nondec) base = get_numbase(cp1, use_locale); - if (! mpg_maybe_float(cp1, use_locale)) { - mpg_zero(n); + if (! mpfp_maybe_float(cp1, use_locale)) { + mpfp_init_zero(n); /* GMP integer */ errno = 0; - mpg_strtoui(n->mpg_i, cp1, cpend - cp1, & ptr, base); + mpfp_strtoui(n->qnumbr, cp1, cpend - cp1, & ptr, base); if (*cp == '-') - mpz_neg(n->mpg_i, n->mpg_i); + mpz_neg(n->qnumbr, n->qnumbr); goto done; } - if (is_mpg_integer(n)) { - mpz_clear(n->mpg_i); + if (is_mpfp_integer(n)) { + mpz_clear(n->qnumbr); + efree(n->qnumbr); n->flags &= ~MPZN; } - if (! is_mpg_float(n)) { - mpfr_init(n->mpg_numbr); + if (! is_mpfp_float(n)) { + emalloc(n->qnumbr, void *, sizeof (mpfr_t), "mpfp_str2num"); + mpfr_init(n->qnumbr); n->flags |= MPFN; } errno = 0; - tval = mpfr_strtofr(n->mpg_numbr, cp, & ptr, base, ROUND_MODE); - IEEE_FMT(n->mpg_numbr, tval); + tval = mpfr_strtofr(n->qnumbr, cp, & ptr, base, ROUND_MODE); + IEEE_FMT(n->qnumbr, tval); done: /* trailing space is OK for NUMBER */ while (isspace((unsigned char) *ptr)) @@ -335,17 +569,17 @@ done: if (errno == 0 && ptr == cpend) return true; errno = 0; - return false; + return false; } -/* mpg_force_number --- force a value to be a multiple-precision number */ +/* mpfp_force_number --- force a value to be a multiple-precision number */ static NODE * -mpg_force_number(NODE *n) +mpfp_force_number(NODE *n) { unsigned int newflags = 0; - if (is_mpg_number(n) && (n->flags & NUMCUR) != 0) + if (is_mpfp_number(n) && (n->flags & NUMCUR) != 0) return n; if ((n->flags & MAYBE_NUM) != 0) { @@ -353,150 +587,130 @@ mpg_force_number(NODE *n) newflags = NUMBER; } - if (force_mpnum(n, (do_non_decimal_data && ! do_traditional), true)) { + if (mpfp_str2num(n, (do_non_decimal_data && ! do_traditional), true)) { n->flags |= newflags; n->flags |= NUMCUR; } return n; } -/* mpg_format_val --- format a numeric value based on format */ + +/* mpfp_format_val --- format a numeric value based on format */ static NODE * -mpg_format_val(const char *format, int index, NODE *s) +mpfp_format_val(const char *format, int index, NODE *s) { - NODE *dummy[2], *r; - unsigned int oflags; + struct format_spec spec; + struct print_fmt_buf *outb; - /* create dummy node for a sole use of format_tree */ - dummy[1] = s; - oflags = s->flags; + if ((s->flags & STRCUR) != 0) + efree(s->stptr); + free_wstr(s); - if (is_mpg_integer(s) || mpfr_integer_p(s->mpg_numbr)) { + /* XXX: format_spec copied since can be altered in the formatting routine */ + + if (is_mpfp_integer(s) || mpfr_integer_p(s->qnumbr)) { /* integral value, use %d */ - r = format_tree("%d", 2, dummy, 2); + spec = *fmt_list[INT_d_FMT_INDEX].spec; s->stfmt = -1; } else { - r = format_tree(format, fmt_list[index]->stlen, dummy, 2); - assert(r != NULL); + assert(fmt_list[index].spec != NULL); /* or can use fmt_parse() --- XXX */ + spec = *fmt_list[index].spec; s->stfmt = (char) index; } - s->flags = oflags; - s->stlen = r->stlen; - if ((s->flags & STRCUR) != 0) - efree(s->stptr); - s->stptr = r->stptr; - freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */ - - s->flags |= STRCUR; - free_wstr(s); + + outb = get_fmt_buf(); + mpfp_format_printf(s, & spec, outb); + (void) bytes2node(outb, s); + free_fmt_buf(outb); + + s->stptr[s->stlen] = '\0'; + s->flags |= STRCUR; return s; } -/* mpg_cmp --- compare two numbers */ +/* mpfp_str2node --- create an arbitrary-pecision number from string */ + +static NODE * +mpfp_str2node(char *str, char **endptr, int base, bool is_integer) +{ + NODE *r; -int -mpg_cmp(const NODE *t1, const NODE *t2) + if (is_integer) { + r = mpfp_integer(); + mpfp_strtoui(r->qnumbr, str, strlen(str), endptr, base); + } else { + int tval; + r = mpfp_float(); + tval = mpfr_strtofr(r->qnumbr, str, endptr, base, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); + } + return r; +} + +/* mpfp_free_num --- free all storage allocated for a multiple-precision number */ + +static void +mpfp_free_num(NODE *tmp) +{ + assert((tmp->flags & (MPFN|MPZN)) != 0); + if (is_mpfp_float(tmp)) + mpfr_clear(tmp->qnumbr); + else /* if (is_mpfp_integer(tmp)) */ + mpz_clear(tmp->qnumbr); + efree(tmp->qnumbr); + tmp->qnumbr = NULL; +} + +/* mpfp_compare --- compare two numbers */ + +static int +mpfp_compare(const NODE *t1, const NODE *t2) { /* * For the purposes of sorting, NaN is considered greater than * any other value, and all NaN values are considered equivalent and equal. */ - if (is_mpg_float(t1)) { - if (is_mpg_float(t2)) { - if (mpfr_nan_p(t1->mpg_numbr)) - return ! mpfr_nan_p(t2->mpg_numbr); - if (mpfr_nan_p(t2->mpg_numbr)) + if (is_mpfp_float(t1)) { + if (is_mpfp_float(t2)) { + if (mpfr_nan_p(MPFR_T(t1->qnumbr))) + return ! mpfr_nan_p(MPFR_T(t2->qnumbr)); + if (mpfr_nan_p(MPFR_T(t2->qnumbr))) return -1; - return mpfr_cmp(t1->mpg_numbr, t2->mpg_numbr); + return mpfr_cmp(t1->qnumbr, t2->qnumbr); } - if (mpfr_nan_p(t1->mpg_numbr)) + if (mpfr_nan_p(MPFR_T(t1->qnumbr))) return 1; - return mpfr_cmp_z(t1->mpg_numbr, t2->mpg_i); - } else if (is_mpg_float(t2)) { + return mpfr_cmp_z(t1->qnumbr, t2->qnumbr); + } else if (is_mpfp_float(t2)) { int ret; - if (mpfr_nan_p(t2->mpg_numbr)) + + if (mpfr_nan_p(MPFR_T(t2->qnumbr))) return -1; - ret = mpfr_cmp_z(t2->mpg_numbr, t1->mpg_i); + ret = mpfr_cmp_z(t2->qnumbr, t1->qnumbr); return ret > 0 ? -1 : (ret < 0); - } else if (is_mpg_integer(t1)) { - return mpz_cmp(t1->mpg_i, t2->mpg_i); } - - /* t1 and t2 are AWKNUMs */ - return cmp_awknums(t1, t2); + assert(is_mpfp_integer(t1) == true); + return mpz_cmp(t1->qnumbr, t2->qnumbr); } +/* mpfp_init_vars --- set PREC and ROUNDMODE defaults */ -/* - * mpg_update_var --- update NR or FNR. - * NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) - */ - -NODE * -mpg_update_var(NODE *n) +static void +mpfp_init_vars() { - NODE *val = n->var_value; - long nr = 0; - mpz_ptr nq = 0; - - if (n == NR_node) { - nr = NR; - nq = MNR; - } else if (n == FNR_node) { - nr = FNR; - nq = MFNR; - } else - cant_happen(); - - if (mpz_sgn(nq) == 0) { - /* Efficiency hack similar to that for AWKNUM */ - if (is_mpg_float(val) || mpz_get_si(val->mpg_i) != nr) { - unref(n->var_value); - val = n->var_value = mpg_integer(); - mpz_set_si(val->mpg_i, nr); - } - } else { - unref(n->var_value); - val = n->var_value = mpg_integer(); - mpz_set_si(val->mpg_i, nr); - mpz_addmul_ui(val->mpg_i, nq, LONG_MAX); /* val->mpg_i += nq * LONG_MAX */ - } - return val; + unref(PREC_node->var_value); + PREC_node->var_value = mpfp_make_number(DEFAULT_PREC); + unref(ROUNDMODE_node->var_value); + ROUNDMODE_node->var_value = make_string(DEFAULT_ROUNDMODE, strlen(DEFAULT_ROUNDMODE)); } -/* mpg_set_var --- set NR or FNR */ -long -mpg_set_var(NODE *n) -{ - long nr = 0; - mpz_ptr nq = 0, r; - NODE *val = n->var_value; - - if (n == NR_node) - nq = MNR; - else if (n == FNR_node) - nq = MFNR; - else - cant_happen(); - - if (is_mpg_integer(val)) - r = val->mpg_i; - else { - /* convert float to integer */ - mpfr_get_z(mpzval, val->mpg_numbr, MPFR_RNDZ); - r = mpzval; - } - nr = mpz_fdiv_q_ui(nq, r, LONG_MAX); /* nq (MNR or MFNR) is quotient */ - return nr; /* remainder (NR or FNR) */ -} +/* mpfp_set_PREC --- update MPFR PRECISION related variables when PREC assigned to */ -/* set_PREC --- update MPFR PRECISION related variables when PREC assigned to */ - -void -set_PREC() +static void +mpfp_set_PREC(const NODE *var) { long prec = 0; NODE *val; @@ -520,12 +734,9 @@ set_PREC() */ }; - if (! do_mpfr) - return; - - val = PREC_node->var_value; + val = var->var_value; if ((val->flags & MAYBE_NUM) != 0) - force_number(val); + (void) force_number(val); if ((val->flags & (STRING|NUMBER)) == STRING) { int i, j; @@ -546,7 +757,6 @@ set_PREC() */ max_exp = ieee_fmts[i].emax; min_exp = ieee_fmts[i].emin; - do_ieee_fmt = true; } } @@ -555,7 +765,7 @@ set_PREC() force_number(val); prec = get_number_si(val); if (prec < MPFR_PREC_MIN || prec > MPFR_PREC_MAX) { - force_string(val); + (void) force_string(val); warning(_("PREC value `%.*s' is invalid"), (int) val->stlen, val->stptr); prec = 0; } else @@ -567,10 +777,10 @@ set_PREC() } -/* get_rnd_mode --- convert string to MPFR rounding mode */ +/* mpfp_get_rounding_mode --- convert string to MPFR rounding mode */ static mpfr_rnd_t -get_rnd_mode(const char rmode) +mpfp_get_rounding_mode(const char rmode) { switch (rmode) { case 'N': @@ -597,32 +807,116 @@ get_rnd_mode(const char rmode) } /* - * set_ROUNDMODE --- update MPFR rounding mode related variables + * mpfp_set_ROUNDMODE --- update MPFR rounding mode related variables * when ROUNDMODE assigned to */ -void -set_ROUNDMODE() -{ - if (do_mpfr) { - mpfr_rnd_t rndm = -1; - NODE *n; - n = force_string(ROUNDMODE_node->var_value); - if (n->stlen == 1) - rndm = get_rnd_mode(n->stptr[0]); - if (rndm != -1) { - mpfr_set_default_rounding_mode(rndm); - ROUND_MODE = rndm; - } else - warning(_("RNDMODE value `%.*s' is invalid"), (int) n->stlen, n->stptr); +static void +mpfp_set_ROUNDMODE(const NODE *var) +{ + mpfr_rnd_t rndmode = -1; + NODE *val; + + val = force_string(var->var_value); + if (val->stlen == 1) + rndmode = mpfp_get_rounding_mode(val->stptr[0]); + if (rndmode != -1) { + mpfr_set_default_rounding_mode(rndmode); + ROUND_MODE = rndmode; + } else + warning(_("ROUNDMODE value `%.*s' is invalid"), (int) val->stlen, val->stptr); +} + +/* + * mpfp_update_var --- update NR or FNR. + * NR_node->var_value(mpz_t) = MNR(mpz_t) * LONG_MAX + NR(long) + */ + +static NODE * +mpfp_update_var(NODE *n) +{ + NODE *val = n->var_value; + long nr = 0; + mpz_ptr nq = 0; + + if (n == NR_node) { + nr = NR; + nq = MNR; + } else { + assert(n == FNR_node); + nr = FNR; + nq = MFNR; + } + + if (mpz_sgn(nq) == 0) { + /* Efficiency hack similar to that for AWKNUM */ + if (is_mpfp_float(val) || mpz_get_si(val->qnumbr) != nr) { + unref(val); + val = n->var_value = mpfp_integer(); + mpz_set_si(val->qnumbr, nr); + } + } else { + unref(val); + val = n->var_value = mpfp_integer(); + mpz_set_si(val->qnumbr, nr); + mpz_addmul_ui(val->qnumbr, nq, LONG_MAX); /* val->mpg_i += nq * LONG_MAX */ + } + return val; +} + +/* mpfp_set_var --- set internal variables */ + +static void +mpfp_set_var(const NODE *var) +{ + if (var == PREC_node) + mpfp_set_PREC(var); + else if (var == ROUNDMODE_node) + mpfp_set_ROUNDMODE(var); + else { + NODE *val = var->var_value; + mpz_ptr r; + mpz_t mpz_val; + + if (is_mpfp_integer(val)) + r = val->qnumbr; + else { + /* convert float to integer */ + mpz_init(mpz_val); + mpfr_get_z(mpz_val, val->qnumbr, MPFR_RNDZ); + r = mpz_val; + } + + if (var == NR_node) + NR = mpz_fdiv_q_ui(MNR, r, LONG_MAX); /* MNR is quotient */ + else + FNR = mpz_fdiv_q_ui(MFNR, r, LONG_MAX); + + if (r != val->qnumbr) + mpz_clear(mpz_val); } } +/* mpfp_increment_var --- increment NR or FNR */ -/* format_ieee --- make sure a number follows IEEE-754 floating-point standard */ +static long +mpfp_increment_var(const NODE *var, long nr) +{ + if (nr == LONG_MAX - 1) { + /* increment quotient, set remainder(NR or FNR) to 0 */ + if (var == NR_node) + mpz_add_ui(MNR, MNR, 1); + else /* if (var == FNR_node) */ + mpz_add_ui(MFNR, MFNR, 1); + return 0; + } + return ++nr; +} -int -format_ieee(mpfr_ptr x, int tval) +/* mpfp_format_ieee --- make sure a number follows IEEE-754 floating-point standard */ + +static int +mpfp_format_ieee(mpfr_ptr x, int tval) { /* * The MPFR doc says that it's our responsibility to make sure all numbers @@ -663,11 +957,40 @@ format_ieee(mpfr_ptr x, int tval) return tval; } +/* mpfp_negate_num --- negate a number in NODE */ -/* do_mpfr_atan2 --- do the atan2 function */ +static void +mpfp_negate_num(NODE *n) +{ + if (is_mpfp_float(n)) { + int tval; + tval = mpfr_neg(n->qnumbr, n->qnumbr, ROUND_MODE); + IEEE_FMT(n->qnumbr, tval); + } else { + /* GMP integer */ + if (mpz_sgn(MPZ_T(n->qnumbr)) == 0) { + /* + * The result should be -0.0, a float. + * XXX: atan2(0, -0) is PI not 0. + */ + mpz_clear(n->qnumbr); + efree(n->qnumbr); + n->flags &= ~MPZN; + emalloc(n->qnumbr, void *, sizeof (mpfr_t), "mpfp_negate_num"); + mpfr_init(n->qnumbr); + n->flags |= MPFN; + + /* XXX: assuming IEEE 754 double, or could use mpfr_set_str(op, "-0.0", ...) */ + mpfr_set_d(n->qnumbr, -0.0, ROUND_MODE); + } else + mpz_neg(n->qnumbr, n->qnumbr); + } +} + +/* do_mpfp_atan2 --- do the atan2 function */ -NODE * -do_mpfr_atan2(int nargs) +static NODE * +do_mpfp_atan2(int nargs) { NODE *t1, *t2, *res; mpfr_ptr p1, p2; @@ -682,124 +1005,132 @@ do_mpfr_atan2(int nargs) if ((t2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("atan2: received non-numeric second argument")); } - force_number(t1); - force_number(t2); - p1 = MP_FLOAT(t1); - p2 = MP_FLOAT(t2); - res = mpg_float(); + (void) force_number(t1); + (void) force_number(t2); + + p1 = mpfp_tofloat(t1, _mp1); + p2 = mpfp_tofloat(t2, _mp2); + + res = mpfp_float(); /* See MPFR documentation for handling of special values like +inf as an argument */ - tval = mpfr_atan2(res->mpg_numbr, p1, p2, ROUND_MODE); - IEEE_FMT(res->mpg_numbr, tval); + tval = mpfr_atan2(res->qnumbr, p1, p2, ROUND_MODE); + IEEE_FMT(res->qnumbr, tval); DEREF(t1); DEREF(t2); return res; } -/* do_mpfr_func --- run an MPFR function - not inline, for debugging */ +/* do_mpfp_func --- run an MPFR function - not inline, for debugging */ static inline NODE * -do_mpfr_func(const char *name, +do_mpfp_func(const char *name, int (*mpfr_func)(), /* putting argument types just gets the compiler confused */ int nargs) { NODE *t1, *res; mpfr_ptr p1; int tval; + int prec; t1 = POP_SCALAR(); if (do_lint && (t1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("%s: received non-numeric argument"), name); force_number(t1); - p1 = MP_FLOAT(t1); - res = mpg_float(); - mpfr_set_prec(res->mpg_numbr, mpfr_get_prec(p1)); /* needed at least for sqrt() */ - tval = mpfr_func(res->mpg_numbr, p1, ROUND_MODE); - IEEE_FMT(res->mpg_numbr, tval); + + if (is_mpfp_integer(t1)) + p1 = mpfp_tofloat(t1, _mp1); + else + p1 = MPFR_T(t1); + + res = mpfp_float(); + prec = mpfr_get_prec(p1); + mpfr_set_prec(res->qnumbr, prec); /* needed at least for sqrt() */ + tval = mpfr_func(res->qnumbr, p1, ROUND_MODE); + IEEE_FMT(res->qnumbr, tval); DEREF(t1); return res; } -#define SPEC_MATH(X) \ +#define MPFPFUNC(X) \ NODE *result; \ -result = do_mpfr_func(#X, mpfr_##X, nargs); \ +result = do_mpfp_func(#X, mpfr_##X, nargs); \ return result -/* do_mpfr_sin --- do the sin function */ +/* do_mpfp_sin --- do the sin function */ -NODE * -do_mpfr_sin(int nargs) +static NODE * +do_mpfp_sin(int nargs) { - SPEC_MATH(sin); + MPFPFUNC(sin); } -/* do_mpfr_cos --- do the cos function */ +/* do_mpfp_cos --- do the cos function */ -NODE * -do_mpfr_cos(int nargs) +static NODE * +do_mpfp_cos(int nargs) { - SPEC_MATH(cos); + MPFPFUNC(cos); } -/* do_mpfr_exp --- exponential function */ +/* do_mpfp_exp --- exponential function */ -NODE * -do_mpfr_exp(int nargs) +static NODE * +do_mpfp_exp(int nargs) { - SPEC_MATH(exp); + MPFPFUNC(exp); } -/* do_mpfr_log --- the log function */ +/* do_mpfp_log --- the log function */ -NODE * -do_mpfr_log(int nargs) +static NODE * +do_mpfp_log(int nargs) { - SPEC_MATH(log); + MPFPFUNC(log); } -/* do_mpfr_sqrt --- do the sqrt function */ +/* do_mpfp_sqrt --- do the sqrt function */ -NODE * -do_mpfr_sqrt(int nargs) +static NODE * +do_mpfp_sqrt(int nargs) { - SPEC_MATH(sqrt); + MPFPFUNC(sqrt); } -/* do_mpfr_int --- convert double to int for awk */ +/* do_mpfp_int --- convert floating point number to integer for awk */ -NODE * -do_mpfr_int(int nargs) +static NODE * +do_mpfp_int(int nargs) { NODE *tmp, *r; tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("int: received non-numeric argument")); - force_number(tmp); + tmp = force_number(tmp); - if (is_mpg_integer(tmp)) { - r = mpg_integer(); - mpz_set(r->mpg_i, tmp->mpg_i); + if (is_mpfp_integer(tmp)) { + r = mpfp_integer(); + mpz_set(r->qnumbr, tmp->qnumbr); } else { - if (! mpfr_number_p(tmp->mpg_numbr)) { + if (! mpfr_number_p(tmp->qnumbr)) { /* [+-]inf or NaN */ return tmp; } - - r = mpg_integer(); - mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ); + r = mpfp_integer(); + mpfr_get_z(r->qnumbr, tmp->qnumbr, MPFR_RNDZ); } DEREF(tmp); return r; } -/* do_mpfr_compl --- perform a ~ operation */ +/* do_mpfp_compl --- perform a ~ operation */ -NODE * -do_mpfr_compl(int nargs) +static NODE * +do_mpfp_compl(int nargs) { NODE *tmp, *r; mpz_ptr zptr; @@ -808,9 +1139,9 @@ do_mpfr_compl(int nargs) if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("compl: received non-numeric argument")); - force_number(tmp); - if (is_mpg_float(tmp)) { - mpfr_ptr p = tmp->mpg_numbr; + (void) force_number(tmp); + if (is_mpfp_float(tmp)) { + mpfr_ptr p = tmp->qnumbr; if (! mpfr_number_p(p)) { /* [+-]inf or NaN */ @@ -819,33 +1150,41 @@ do_mpfr_compl(int nargs) if (do_lint) { if (mpfr_sgn(p) < 0) lintwarn("%s", - mpg_fmt(_("compl(%Rg): negative value will give strange results"), p) + mpfp_sprintf(_("compl(%Rg): negative value will give strange results"), p) ); if (! mpfr_integer_p(p)) lintwarn("%s", - mpg_fmt(_("comp(%Rg): fractional value will be truncated"), p) + mpfp_sprintf(_("comp(%Rg): fractional value will be truncated"), p) ); } - - mpfr_get_z(mpzval, p, MPFR_RNDZ); /* float to integer conversion */ - zptr = mpzval; + + emalloc(zptr, mpz_ptr, sizeof (mpz_t), "do_mpfr_compl"); + mpz_init(zptr); + mpfr_get_z(zptr, p, MPFR_RNDZ); /* float to integer conversion */ + } else { /* (tmp->flags & MPZN) != 0 */ - zptr = tmp->mpg_i; + zptr = tmp->qnumbr; if (do_lint) { if (mpz_sgn(zptr) < 0) lintwarn("%s", - mpg_fmt(_("cmpl(%Zd): negative values will give strange results"), zptr) + mpfp_sprintf(_("cmpl(%Zd): negative values will give strange results"), zptr) ); } } - r = mpg_integer(); - mpz_com(r->mpg_i, zptr); + r = mpfp_integer(); + mpz_com(r->qnumbr, zptr); + + if (zptr != tmp->qnumbr) { + mpz_clear(zptr); + efree(zptr); + } DEREF(tmp); return r; } + /* get_intval --- get the (converted) integral operand of a binary function. */ static mpz_ptr @@ -858,16 +1197,15 @@ get_intval(NODE *t1, int argnum, const char *op) (void) force_number(t1); - if (is_mpg_float(t1)) { - mpfr_ptr left = t1->mpg_numbr; + if (is_mpfp_float(t1)) { + mpfr_ptr left = t1->qnumbr; if (! mpfr_number_p(left)) { /* inf or NaN */ if (do_lint) lintwarn("%s", - mpg_fmt(_("%s: argument #%d has invalid value %Rg, using 0"), + mpfp_sprintf(_("%s: argument #%d has invalid value %Rg, using 0"), op, argnum, left) ); - emalloc(pz, mpz_ptr, sizeof (mpz_t), "get_intval"); mpz_init(pz); return pz; /* should be freed */ @@ -876,13 +1214,13 @@ get_intval(NODE *t1, int argnum, const char *op) if (do_lint) { if (mpfr_sgn(left) < 0) lintwarn("%s", - mpg_fmt(_("%s: argument #%d negative value %Rg will give strange results"), + mpfp_sprintf(_("%s: argument #%d negative value %Rg will give strange results"), op, argnum, left) ); if (! mpfr_integer_p(left)) lintwarn("%s", - mpg_fmt(_("%s: argument #%d fractional value %Rg will be truncated"), + mpfp_sprintf(_("%s: argument #%d fractional value %Rg will be truncated"), op, argnum, left) ); } @@ -891,36 +1229,35 @@ get_intval(NODE *t1, int argnum, const char *op) mpz_init(pz); mpfr_get_z(pz, left, MPFR_RNDZ); /* float to integer conversion */ return pz; /* should be freed */ - } + } + /* (t1->flags & MPZN) != 0 */ - pz = t1->mpg_i; + pz = t1->qnumbr; if (do_lint) { if (mpz_sgn(pz) < 0) lintwarn("%s", - mpg_fmt(_("%s: argument #%d negative value %Zd will give strange results"), + mpfp_sprintf(_("%s: argument #%d negative value %Zd will give strange results"), op, argnum, pz) ); } return pz; /* must not be freed */ } - /* free_intval --- free the converted integer value returned by get_intval() */ static inline void free_intval(NODE *t, mpz_ptr pz) { - if ((t->flags & MPZN) == 0) { + if (t->qnumbr != pz) { mpz_clear(pz); efree(pz); } } +/* do_mpfp_lshift --- perform a << operation */ -/* do_mpfr_lshift --- perform a << operation */ - -NODE * -do_mpfr_lshift(int nargs) +static NODE * +do_mpfp_lshift(int nargs) { NODE *t1, *t2, *res; unsigned long shift; @@ -939,8 +1276,8 @@ do_mpfr_lshift(int nargs) */ shift = mpz_get_ui(pz2); /* GMP integer => unsigned long conversion */ - res = mpg_integer(); - mpz_mul_2exp(res->mpg_i, pz1, shift); /* res = pz1 * 2^shift */ + res = mpfp_integer(); + mpz_mul_2exp(res->qnumbr, pz1, shift); /* res = pz1 * 2^shift */ free_intval(t1, pz1); free_intval(t2, pz2); @@ -949,10 +1286,10 @@ do_mpfr_lshift(int nargs) return res; } -/* do_mpfr_rshift --- perform a >> operation */ +/* do_mpfp_rshift --- perform a >> operation */ -NODE * -do_mpfr_rshift(int nargs) +static NODE * +do_mpfp_rshift(int nargs) { NODE *t1, *t2, *res; unsigned long shift; @@ -966,8 +1303,8 @@ do_mpfr_rshift(int nargs) /* N.B: See do_mpfp_lshift. */ shift = mpz_get_ui(pz2); /* GMP integer => unsigned long conversion */ - res = mpg_integer(); - mpz_fdiv_q_2exp(res->mpg_i, pz1, shift); /* res = pz1 / 2^shift, round towards −inf */ + res = mpfp_integer(); + mpz_fdiv_q_2exp(res->qnumbr, pz1, shift); /* res = pz1 / 2^shift, round towards −inf */ free_intval(t1, pz1); free_intval(t2, pz2); @@ -976,11 +1313,10 @@ do_mpfr_rshift(int nargs) return res; } +/* do_mpfp_and --- perform an & operation */ -/* do_mpfr_and --- perform an & operation */ - -NODE * -do_mpfr_and(int nargs) +static NODE * +do_mpfp_and(int nargs) { NODE *t1, *t2, *res; mpz_ptr pz1, pz2; @@ -992,27 +1328,26 @@ do_mpfr_and(int nargs) t2 = POP_SCALAR(); pz2 = get_intval(t2, nargs, "and"); - res = mpg_integer(); + res = mpfp_integer(); for (i = 1; i < nargs; i++) { t1 = POP_SCALAR(); pz1 = get_intval(t1, nargs - i, "and"); - mpz_and(res->mpg_i, pz1, pz2); + mpz_and(res->qnumbr, pz1, pz2); free_intval(t1, pz1); DEREF(t1); if (i == 1) { free_intval(t2, pz2); DEREF(t2); } - pz2 = res->mpg_i; + pz2 = res->qnumbr; } return res; } +/* do_mpfp_or --- perform an | operation */ -/* do_mpfr_or --- perform an | operation */ - -NODE * -do_mpfr_or(int nargs) +static NODE * +do_mpfp_or(int nargs) { NODE *t1, *t2, *res; mpz_ptr pz1, pz2; @@ -1024,26 +1359,26 @@ do_mpfr_or(int nargs) t2 = POP_SCALAR(); pz2 = get_intval(t2, nargs, "or"); - res = mpg_integer(); + res = mpfp_integer(); for (i = 1; i < nargs; i++) { t1 = POP_SCALAR(); pz1 = get_intval(t1, nargs - i, "or"); - mpz_ior(res->mpg_i, pz1, pz2); + mpz_ior(res->qnumbr, pz1, pz2); free_intval(t1, pz1); DEREF(t1); if (i == 1) { free_intval(t2, pz2); DEREF(t2); } - pz2 = res->mpg_i; + pz2 = res->qnumbr; } return res; } -/* do_mpfr_xor --- perform an ^ operation */ +/* do_mpfp_xor --- perform an ^ operation */ -NODE * -do_mpfr_xor(int nargs) +static NODE * +do_mpfp_xor(int nargs) { NODE *t1, *t2, *res; mpz_ptr pz1, pz2; @@ -1055,47 +1390,47 @@ do_mpfr_xor(int nargs) t2 = POP_SCALAR(); pz2 = get_intval(t2, nargs, "xor"); - res = mpg_integer(); + res = mpfp_integer(); for (i = 1; i < nargs; i++) { t1 = POP_SCALAR(); pz1 = get_intval(t1, nargs - i, "xor"); - mpz_xor(res->mpg_i, pz1, pz2); + mpz_xor(res->qnumbr, pz1, pz2); free_intval(t1, pz1); DEREF(t1); if (i == 1) { free_intval(t2, pz2); DEREF(t2); } - pz2 = res->mpg_i; + pz2 = res->qnumbr; } return res; } -/* do_mpfr_strtonum --- the strtonum function */ +/* do_mpfp_strtonum --- the strtonum function */ -NODE * -do_mpfr_strtonum(int nargs) +static NODE * +do_mpfp_strtonum(int nargs) { NODE *tmp, *r; tmp = POP_SCALAR(); if ((tmp->flags & (NUMBER|NUMCUR)) == 0) { - r = mpg_integer(); /* will be changed to MPFR float if necessary in force_mpnum() */ + r = mpfp_integer(); /* will be changed to MPFR float if necessary in force_mpnum() */ r->stptr = tmp->stptr; r->stlen = tmp->stlen; - force_mpnum(r, true, use_lc_numeric); + mpfp_str2num(r, true, use_lc_numeric); r->stptr = NULL; r->stlen = 0; } else { (void) force_number(tmp); - if (is_mpg_float(tmp)) { + if (is_mpfp_float(tmp)) { int tval; - r = mpg_float(); - tval = mpfr_set(r->mpg_numbr, tmp->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + r = mpfp_float(); + tval = mpfr_set(r->qnumbr, (mpfr_ptr) tmp->qnumbr, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } else { - r = mpg_integer(); - mpz_set(r->mpg_i, tmp->mpg_i); + r = mpfp_integer(); + mpz_set(r->qnumbr, tmp->qnumbr); } } @@ -1108,10 +1443,10 @@ static bool firstrand = true; static gmp_randstate_t state; static mpz_t seed; /* current seed */ -/* do_mpfr_rand --- do the rand function */ +/* do_mpfp_rand --- do the rand function */ -NODE * -do_mpfr_rand(int nargs ATTRIBUTE_UNUSED) +static NODE * +do_mpfp_rand(int nargs ATTRIBUTE_UNUSED) { NODE *res; int tval; @@ -1134,17 +1469,17 @@ do_mpfr_rand(int nargs ATTRIBUTE_UNUSED) gmp_randseed(state, seed); firstrand = false; } - res = mpg_float(); - tval = mpfr_urandomb(res->mpg_numbr, state); - IEEE_FMT(res->mpg_numbr, tval); + res = mpfp_float(); + tval = mpfr_urandomb(res->qnumbr, state); + IEEE_FMT(res->qnumbr, tval); return res; } -/* do_mpfr_srand --- seed the random number generator */ +/* do_mpfp_srand --- seed the random number generator */ -NODE * -do_mpfr_srand(int nargs) +static NODE * +do_mpfp_srand(int nargs) { NODE *res; @@ -1166,8 +1501,8 @@ do_mpfr_srand(int nargs) firstrand = false; } - res = mpg_integer(); - mpz_set(res->mpg_i, seed); /* previous seed */ + res = mpfp_integer(); + mpz_set(res->qnumbr, seed); /* previous seed */ if (nargs == 0) mpz_set_ui(seed, (unsigned long) time((time_t *) 0)); @@ -1176,11 +1511,11 @@ do_mpfr_srand(int nargs) tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("srand: received non-numeric argument")); - force_number(tmp); - if (is_mpg_float(tmp)) - mpfr_get_z(seed, tmp->mpg_numbr, MPFR_RNDZ); + (void) force_number(tmp); + if (is_mpfp_float(tmp)) + mpfr_get_z(seed, tmp->qnumbr, MPFR_RNDZ); else /* MP integer */ - mpz_set(seed, tmp->mpg_i); + mpz_set(seed, tmp->qnumbr); DEREF(tmp); } @@ -1188,7 +1523,7 @@ do_mpfr_srand(int nargs) return res; } -/* do_mpfr_div --- do integer division, return quotient and remainder in dest array */ +/* do_mpfp_div --- do integer division, return quotient and remainder in dest array */ /* * We define the semantics as: @@ -1198,8 +1533,8 @@ do_mpfr_srand(int nargs) * remainder = int(numerator % denomator) */ -NODE * -do_mpfr_div(int nargs) +static NODE * +do_mpfp_div(int nargs) { NODE *numerator, *denominator, *result; NODE *num, *denom; @@ -1225,40 +1560,40 @@ do_mpfr_div(int nargs) (void) force_number(denominator); /* convert numerator and denominator to integer */ - if (is_mpg_integer(numerator)) { - num = mpg_integer(); - mpz_set(num->mpg_i, numerator->mpg_i); + if (is_mpfp_integer(numerator)) { + num = mpfp_integer(); + mpz_set(num->qnumbr, numerator->qnumbr); } else { - if (! mpfr_number_p(numerator->mpg_numbr)) { + if (! mpfr_number_p(numerator->qnumbr)) { /* [+-]inf or NaN */ return numerator; } - num = mpg_integer(); - mpfr_get_z(num->mpg_i, numerator->mpg_numbr, MPFR_RNDZ); + num = mpfp_integer(); + mpfr_get_z(num->qnumbr, numerator->qnumbr, MPFR_RNDZ); } - if (is_mpg_integer(denominator)) { - denom = mpg_integer(); - mpz_set(denom->mpg_i, denominator->mpg_i); + if (is_mpfp_integer(denominator)) { + denom = mpfp_integer(); + mpz_set(denom->qnumbr, denominator->qnumbr); } else { - if (! mpfr_number_p(denominator->mpg_numbr)) { + if (! mpfr_number_p(denominator->qnumbr)) { /* [+-]inf or NaN */ return denominator; } - denom = mpg_integer(); - mpfr_get_z(denom->mpg_i, denominator->mpg_numbr, MPFR_RNDZ); + denom = mpfp_integer(); + mpfr_get_z(denom->qnumbr, denominator->qnumbr, MPFR_RNDZ); } - if (mpz_sgn(denom->mpg_i) == 0) + if (mpz_sgn(MPZ_T(denom->qnumbr)) == 0) fatal(_("div: division by zero attempted")); - quotient = mpg_integer(); - remainder = mpg_integer(); + quotient = mpfp_integer(); + remainder = mpfp_integer(); /* do the division */ - mpz_tdiv_qr(quotient->mpg_i, remainder->mpg_i, num->mpg_i, denom->mpg_i); + mpz_tdiv_qr(quotient->qnumbr, remainder->qnumbr, num->qnumbr, denom->qnumbr); unref(num); unref(denom); unref(numerator); @@ -1314,157 +1649,160 @@ mpg_tofloat(mpfr_ptr mf, mpz_ptr mz) } -/* mpg_add --- add arbitrary-precision numbers */ +/* mpfp_add --- add arbitrary-precision numbers */ static NODE * -mpg_add(NODE *t1, NODE *t2) +mpfp_add(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2)) { - r = mpg_integer(); - mpz_add(r->mpg_i, t1->mpg_i, t2->mpg_i); + if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) { + r = mpfp_integer(); + mpz_add(r->qnumbr, t1->qnumbr, t2->qnumbr); } else { - r = mpg_float(); - if (is_mpg_integer(t2)) - tval = mpfr_add_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, ROUND_MODE); - else if (is_mpg_integer(t1)) - tval = mpfr_add_z(r->mpg_numbr, t2->mpg_numbr, t1->mpg_i, ROUND_MODE); + r = mpfp_float(); + if (is_mpfp_integer(t2)) + tval = mpfr_add_z(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + else if (is_mpfp_integer(t1)) + tval = mpfr_add_z(r->qnumbr, t2->qnumbr, t1->qnumbr, ROUND_MODE); else - tval = mpfr_add(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + tval = mpfr_add(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } return r; } -/* mpg_sub --- subtract arbitrary-precision numbers */ +/* mpfp_sub --- subtract arbitrary-precision numbers */ static NODE * -mpg_sub(NODE *t1, NODE *t2) +mpfp_sub(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2)) { - r = mpg_integer(); - mpz_sub(r->mpg_i, t1->mpg_i, t2->mpg_i); + if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) { + r = mpfp_integer(); + mpz_sub(r->qnumbr, t1->qnumbr, t2->qnumbr); } else { - r = mpg_float(); - if (is_mpg_integer(t2)) - tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, ROUND_MODE); - else if (is_mpg_integer(t1)) { + r = mpfp_float(); + if (is_mpfp_integer(t2)) + tval = mpfr_sub_z(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + else if (is_mpfp_integer(t1)) { #if (!defined(MPFR_VERSION) || (MPFR_VERSION < MPFR_VERSION_NUM(3,1,0))) - NODE *tmp = t1; + const NODE *tmp = t1; + t1 = t2; t2 = tmp; - tval = mpfr_sub_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, ROUND_MODE); - tval = mpfr_neg(r->mpg_numbr, r->mpg_numbr, ROUND_MODE); + tval = mpfr_sub_z(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + tval = mpfr_neg(r->qnumbr, r->qnumbr, ROUND_MODE); t2 = t1; t1 = tmp; #else - tval = mpfr_z_sub(r->mpg_numbr, t1->mpg_i, t2->mpg_numbr, ROUND_MODE); + tval = mpfr_z_sub(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); #endif } else - tval = mpfr_sub(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + tval = mpfr_sub(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } return r; } -/* mpg_mul --- multiply arbitrary-precision numbers */ +/* mpfp_mul --- multiply arbitrary-precision numbers */ static NODE * -mpg_mul(NODE *t1, NODE *t2) +mpfp_mul(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2)) { - r = mpg_integer(); - mpz_mul(r->mpg_i, t1->mpg_i, t2->mpg_i); + if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) { + r = mpfp_integer(); + mpz_mul(r->qnumbr, t1->qnumbr, t2->qnumbr); } else { - r = mpg_float(); - if (is_mpg_integer(t2)) - tval = mpfr_mul_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, ROUND_MODE); - else if (is_mpg_integer(t1)) - tval = mpfr_mul_z(r->mpg_numbr, t2->mpg_numbr, t1->mpg_i, ROUND_MODE); + r = mpfp_float(); + if (is_mpfp_integer(t2)) + tval = mpfr_mul_z(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + else if (is_mpfp_integer(t1)) + tval = mpfr_mul_z(r->qnumbr, t2->qnumbr, t1->qnumbr, ROUND_MODE); else - tval = mpfr_mul(r->mpg_numbr, t1->mpg_numbr, t2->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + tval = mpfr_mul(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } return r; } - -/* mpg_pow --- exponentiation involving arbitrary-precision numbers */ +/* mpfp_pow --- exponentiation involving arbitrary-precision numbers */ static NODE * -mpg_pow(NODE *t1, NODE *t2) +mpfp_pow(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2)) { - if (mpz_sgn(t2->mpg_i) >= 0 && mpz_fits_ulong_p(t2->mpg_i)) { - r = mpg_integer(); - mpz_pow_ui(r->mpg_i, t1->mpg_i, mpz_get_ui(t2->mpg_i)); + if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) { + if (mpz_sgn(MPZ_T(t2->qnumbr)) >= 0 && mpz_fits_ulong_p(t2->qnumbr)) { + r = mpfp_integer(); + mpz_pow_ui(r->qnumbr, t1->qnumbr, mpz_get_ui(t2->qnumbr)); } else { mpfr_ptr p1, p2; - p1 = MP_FLOAT(t1); - p2 = MP_FLOAT(t2); - r = mpg_float(); - tval = mpfr_pow(r->mpg_numbr, p1, p2, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + + p1 = mpfp_tofloat(t1, _mp1); + p2 = mpfp_tofloat(t2, _mp2); + r = mpfp_float(); + tval = mpfr_pow(r->qnumbr, p1, p2, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } } else { - r = mpg_float(); - if (is_mpg_integer(t2)) - tval = mpfr_pow_z(r->mpg_numbr, t1->mpg_numbr, t2->mpg_i, ROUND_MODE); + r = mpfp_float(); + if (is_mpfp_integer(t2)) + tval = mpfr_pow_z(r->qnumbr, t1->qnumbr, t2->qnumbr, ROUND_MODE); else { mpfr_ptr p1; - p1 = MP_FLOAT(t1); - tval = mpfr_pow(r->mpg_numbr, p1, t2->mpg_numbr, ROUND_MODE); + + p1 = mpfp_tofloat(t1, _mp1); + tval = mpfr_pow(r->qnumbr, p1, t2->qnumbr, ROUND_MODE); } - IEEE_FMT(r->mpg_numbr, tval); + IEEE_FMT(r->qnumbr, tval); } return r; } -/* mpg_div --- arbitrary-precision division */ +/* mpfp_div --- arbitrary-precision division */ static NODE * -mpg_div(NODE *t1, NODE *t2) +mpfp_div(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2) - && (mpz_sgn(t2->mpg_i) != 0) /* not dividing by 0 */ - && mpz_divisible_p(t1->mpg_i, t2->mpg_i) + if (is_mpfp_integer(t1) && is_mpfp_integer(t2) + && (mpz_sgn(MPZ_T(t2->qnumbr)) != 0) /* not dividing by 0 */ + && mpz_divisible_p(t1->qnumbr, t2->qnumbr) ) { - r = mpg_integer(); - mpz_divexact(r->mpg_i, t1->mpg_i, t2->mpg_i); + r = mpfp_integer(); + mpz_divexact(r->qnumbr, t1->qnumbr, t2->qnumbr); } else { mpfr_ptr p1, p2; - p1 = MP_FLOAT(t1); - p2 = MP_FLOAT(t2); - r = mpg_float(); - tval = mpfr_div(r->mpg_numbr, p1, p2, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + + p1 = mpfp_tofloat(t1, _mp1); + p2 = mpfp_tofloat(t2, _mp2); + r = mpfp_float(); + tval = mpfr_div(r->qnumbr, p1, p2, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } return r; } -/* mpg_mod --- modulus operation with arbitrary-precision numbers */ +/* mpfp_mod --- modulus operation with arbitrary-precision numbers */ static NODE * -mpg_mod(NODE *t1, NODE *t2) +mpfp_mod(const NODE *t1, const NODE *t2) { NODE *r; int tval; - if (is_mpg_integer(t1) && is_mpg_integer(t2)) { + if (is_mpfp_integer(t1) && is_mpfp_integer(t2)) { /* * 8/2014: Originally, this was just * @@ -1482,259 +1820,70 @@ mpg_mod(NODE *t1, NODE *t2) */ NODE *dummy_quotient; - r = mpg_integer(); - dummy_quotient = mpg_integer(); - mpz_tdiv_qr(dummy_quotient->mpg_i, r->mpg_i, t1->mpg_i, t2->mpg_i); + r = mpfp_integer(); + dummy_quotient = mpfp_integer(); + mpz_tdiv_qr(dummy_quotient->qnumbr, r->qnumbr, t1->qnumbr, t2->qnumbr); unref(dummy_quotient); } else { mpfr_ptr p1, p2; - p1 = MP_FLOAT(t1); - p2 = MP_FLOAT(t2); - r = mpg_float(); - tval = mpfr_fmod(r->mpg_numbr, p1, p2, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); + + p1 = mpfp_tofloat(t1, _mp1); + p2 = mpfp_tofloat(t2, _mp2); + r = mpfp_float(); + tval = mpfr_fmod(r->qnumbr, p1, p2, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } return r; } - -/* - * mpg_interpret --- pre-exec hook in the interpreter. Handles - * arithmetic operations with MPFR/GMP numbers. - */ - -static int -mpg_interpret(INSTRUCTION **cp) -{ - INSTRUCTION *pc = *cp; /* current instruction */ - OPCODE op; /* current opcode */ - NODE *r = NULL; - NODE *t1, *t2; - NODE **lhs; - int tval; /* the ternary value returned by a MPFR function */ - - switch ((op = pc->opcode)) { - case Op_plus_i: - t2 = force_number(pc->memory); - goto plus; - case Op_plus: - t2 = POP_NUMBER(); -plus: - t1 = TOP_NUMBER(); - r = mpg_add(t1, t2); - DEREF(t1); - if (op == Op_plus) - DEREF(t2); - REPLACE(r); - break; - case Op_minus_i: - t2 = force_number(pc->memory); - goto minus; - case Op_minus: - t2 = POP_NUMBER(); -minus: - t1 = TOP_NUMBER(); - r = mpg_sub(t1, t2); - DEREF(t1); - if (op == Op_minus) - DEREF(t2); - REPLACE(r); - break; - - case Op_times_i: - t2 = force_number(pc->memory); - goto times; - case Op_times: - t2 = POP_NUMBER(); -times: - t1 = TOP_NUMBER(); - r = mpg_mul(t1, t2); - DEREF(t1); - if (op == Op_times) - DEREF(t2); - REPLACE(r); - break; - - case Op_exp_i: - t2 = force_number(pc->memory); - goto exp; - case Op_exp: - t2 = POP_NUMBER(); -exp: - t1 = TOP_NUMBER(); - r = mpg_pow(t1, t2); - DEREF(t1); - if (op == Op_exp) - DEREF(t2); - REPLACE(r); - break; - - case Op_quotient_i: - t2 = force_number(pc->memory); - goto quotient; - case Op_quotient: - t2 = POP_NUMBER(); -quotient: - t1 = TOP_NUMBER(); - r = mpg_div(t1, t2); - DEREF(t1); - if (op == Op_quotient) - DEREF(t2); - REPLACE(r); - break; - - case Op_mod_i: - t2 = force_number(pc->memory); - goto mod; - case Op_mod: - t2 = POP_NUMBER(); -mod: - t1 = TOP_NUMBER(); - r = mpg_mod(t1, t2); - DEREF(t1); - if (op == Op_mod) - DEREF(t2); - REPLACE(r); - break; +/* mpfp_add_long --- add aribitary-precision number to long */ - case Op_preincrement: - case Op_predecrement: - lhs = TOP_ADDRESS(); - t1 = *lhs; - force_number(t1); - - if (is_mpg_integer(t1)) { - if (t1->valref == 1 && t1->flags == (MALLOC|MPZN|NUMCUR|NUMBER)) - /* Efficiency hack. Big speed-up (> 30%) in a tight loop */ - r = t1; - else - r = *lhs = mpg_integer(); - if (op == Op_preincrement) - mpz_add_ui(r->mpg_i, t1->mpg_i, 1); - else - mpz_sub_ui(r->mpg_i, t1->mpg_i, 1); - } else { +static NODE * +mpfp_add_long(const NODE *t1, long l) +{ + NODE *r; - /* - * An optimization like the one above is not going to work - * for a floating-point number. With it, - * gawk -M 'BEGIN { PREC=53; i=2^53+0.0; PREC=113; ++i; print i}' - * will output 2^53 instead of 2^53+1. - */ - - r = *lhs = mpg_float(); - tval = mpfr_add_si(r->mpg_numbr, t1->mpg_numbr, - op == Op_preincrement ? 1 : -1, - ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - } - if (r != t1) - unref(t1); - UPREF(r); - REPLACE(r); - break; + if (is_mpfp_integer(t1)) { + r = mpfp_integer(); + if (l >= 0) + mpz_add_ui(r->qnumbr, t1->qnumbr, l); + else + mpz_sub_ui(r->qnumbr, t1->qnumbr, -l); + } else { + int tval; - case Op_postincrement: - case Op_postdecrement: - lhs = TOP_ADDRESS(); - t1 = *lhs; - force_number(t1); - - if (is_mpg_integer(t1)) { - r = mpg_integer(); - mpz_set(r->mpg_i, t1->mpg_i); - if (t1->valref == 1 && t1->flags == (MALLOC|MPZN|NUMCUR|NUMBER)) - /* Efficiency hack. Big speed-up (> 30%) in a tight loop */ - t2 = t1; - else - t2 = *lhs = mpg_integer(); - if (op == Op_postincrement) - mpz_add_ui(t2->mpg_i, t1->mpg_i, 1); - else - mpz_sub_ui(t2->mpg_i, t1->mpg_i, 1); - } else { - r = mpg_float(); - tval = mpfr_set(r->mpg_numbr, t1->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - t2 = *lhs = mpg_float(); - tval = mpfr_add_si(t2->mpg_numbr, t1->mpg_numbr, - op == Op_postincrement ? 1 : -1, - ROUND_MODE); - IEEE_FMT(t2->mpg_numbr, tval); - } - if (t2 != t1) - unref(t1); - REPLACE(r); - break; + r = mpfp_float(); + tval = mpfr_add_si(r->qnumbr, t1->qnumbr, l, ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); + } + return r; +} - case Op_unary_minus: - t1 = TOP_NUMBER(); - if (is_mpg_float(t1)) { - r = mpg_float(); - tval = mpfr_neg(r->mpg_numbr, t1->mpg_numbr, ROUND_MODE); - IEEE_FMT(r->mpg_numbr, tval); - } else { - r = mpg_integer(); - mpz_neg(r->mpg_i, t1->mpg_i); - } - DEREF(t1); - REPLACE(r); - break; +/* mpfp_copy_number --- copy an arbitrary-precision number */ - case Op_assign_plus: - case Op_assign_minus: - case Op_assign_times: - case Op_assign_quotient: - case Op_assign_mod: - case Op_assign_exp: - lhs = POP_ADDRESS(); - t1 = *lhs; - force_number(t1); - t2 = TOP_NUMBER(); - - switch (op) { - case Op_assign_plus: - r = mpg_add(t1, t2); - break; - case Op_assign_minus: - r = mpg_sub(t1, t2); - break; - case Op_assign_times: - r = mpg_mul(t1, t2); - break; - case Op_assign_quotient: - r = mpg_div(t1, t2); - break; - case Op_assign_mod: - r = mpg_mod(t1, t2); - break; - case Op_assign_exp: - r = mpg_pow(t1, t2); - break; - default: - cant_happen(); - } +static NODE * +mpfp_copy_number(const NODE *n) +{ + NODE *r; - DEREF(t2); - unref(*lhs); - *lhs = r; - UPREF(r); - REPLACE(r); - break; + if (is_mpfp_integer(n)) { + r = mpfp_integer(); + mpz_set(r->qnumbr, n->qnumbr); + } else { + int tval; - default: - return true; /* unhandled */ + r = mpfp_float(); + tval = mpfr_set(r->qnumbr, MPFR_T(n->qnumbr), ROUND_MODE); + IEEE_FMT(r->qnumbr, tval); } - - *cp = pc->nexti; /* next instruction to execute */ - return false; + return r; } -/* mpg_fmt --- output formatted string with special MPFR/GMP conversion specifiers */ +/* mpfp_sprintf --- output formatted string with special MPFR/GMP conversion specifiers */ -const char * -mpg_fmt(const char *mesg, ...) +static const char * +mpfp_sprintf(const char *mesg, ...) { static char *tmp = NULL; int ret; @@ -1752,34 +1901,289 @@ mpg_fmt(const char *mesg, ...) return mesg; } -/* mpfr_unset --- clear out the MPFR values */ -void -mpfr_unset(NODE *n) -{ - if (is_mpg_float(n)) - mpfr_clear(n->mpg_numbr); - else if (is_mpg_integer(n)) - mpz_clear(n->mpg_i); -} +/* + * mpz2mpfr --- convert an arbitrary-precision integer to a float + * without any loss of precision. If the 2nd arg is NULL, the returned MPFR + * value should be freed when done: + * mpfr_clear(mpfrval); efree(mpfrval); + * If the 2nd arg is not NULL, it is assumed that the MPFR variable has + * already been initialized. + */ -#else -void -set_PREC() +static mpfr_ptr +mpz2mpfr(mpz_ptr mpz_val, mpfr_ptr mpfr_val) { - /* dummy function */ + long prec, prec1; + int tval; + + /* + * When implicitely converting a GMP integer operand to a MPFR float, use + * a precision sufficiently large to hold the converted value exactly. + * + * $ ./gawk -M 'BEGIN { print 13 % 2 }' + * 1 + * If the user-specified precision is used to convert the integer 13 to a + * float, one will get: + * $ ./gawk -M 'BEGIN { PREC=2; print 13 % 2.0 }' + * 0 + */ + + /* estimate minimum precision for exact conversion */ + prec = mpz_sizeinbase(mpz_val, 2); /* most significant 1 bit position starting at 1 */ + + prec1 = prec; /* silence "prec1 may not be initialized" warnings */ + if (mpfr_val != NULL && (prec1 = mpfr_get_prec(mpfr_val)) >= prec) + goto finish; + + prec -= (long) mpz_scan1(mpz_val, 0); /* least significant 1 bit index starting at 0 */ + if (prec < MPFR_PREC_MIN) + prec = MPFR_PREC_MIN; + else if (prec > MPFR_PREC_MAX) + prec = MPFR_PREC_MAX; + + if (mpfr_val == NULL) { + emalloc(mpfr_val, mpfr_ptr, sizeof (mpfr_t), "mpz2mpfr"); + mpfr_init2(mpfr_val, prec); + } else if (prec > prec1) + mpfr_set_prec(mpfr_val, prec); + +finish: + + tval = mpfr_set_z(mpfr_val, mpz_val, ROUND_MODE); + IEEE_FMT(mpfr_val, tval); + return mpfr_val; } -void -set_ROUNDMODE() +/* mpfp_format_prinf --- format a number for (s)printf */ + +static int +mpfp_format_printf(NODE *arg, struct format_spec *spec, struct print_fmt_buf *outb) { - /* dummy function */ + mpz_ptr zi = NULL; + mpfr_ptr mf = NULL; + enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } mpfmt_spec; + + uintmax_t uval; + char *cp; + char cs1; + int nc; + +# define CP cpbuf_start(outb) +# define CEND cpbuf_end(outb) +# define CPBUF cpbuf(outb) + + spec->fill = space_string; + spec->chbuf = lchbuf; + + cs1 = spec->fmtchar; + cp = CP; + + switch (cs1) { + case 'd': + case 'i': + if (is_mpfp_float(arg)) + goto mpf0; + goto mpz0; + case 'X': + spec->chbuf = Uchbuf; + /* FALL THROUGH */ + case 'x': + /* FALL THROUGH */ + case 'u': + /* FALL THROUGH */ + case 'o': + if (is_mpfp_integer(arg)) { +mpz0: + zi = arg->qnumbr; + + if (cs1 != 'd' && cs1 != 'i') { + if (mpz_sgn(zi) <= 0) { + /* + * Negative value or 0 requires special handling. + * Unlike MPFR, GMP does not allow conversion + * to (u)intmax_t. So we first convert GMP type to + * a MPFR type. + */ + mf = mpz2mpfr(zi, _mpfrval); + goto mpf1; + } + spec->signchar = '\0'; /* Don't print '+' */ + } + + /* See comments above about when to fill with zeros */ + spec->zero_flag = (! spec->lj + && ((spec->zero_flag && ! spec->have_prec) + || (spec->fw == 0 && spec->have_prec))); + + mpfmt_spec = spec->have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; + goto fmt0; + + } + + assert(is_mpfp_float(arg) == true); +mpf0: + mf = arg->qnumbr; + if (! mpfr_number_p(mf)) { + /* inf or NaN */ + cs1 = 'g'; + mpfmt_spec = MP_FLOAT; + goto fmt1; + } + + if (cs1 != 'd' && cs1 != 'i') { +mpf1: + /* + * The output of printf("%#.0x", 0) is 0 instead of 0x, hence <= in + * the comparison below. + */ + if (mpfr_sgn(mf) <= 0) { + if (! mpfr_fits_intmax_p(mf, ROUND_MODE)) { + /* -ve number is too large */ + cs1 = 'g'; + mpfmt_spec = MP_FLOAT; + goto fmt1; + } + + uval = (uintmax_t) mpfr_get_sj(mf, ROUND_MODE); + if (! spec->alt && spec->have_prec && spec->prec == 0 && uval == 0) { + /* printf("%.0x", 0) is no characters */ + pr_num_tail(cp, 0, spec, outb); + } else + format_nondecimal(uval, spec, outb); + return 0; + } + spec->signchar = '\0'; /* Don't print '+' */ + } + + /* See comments above about when to fill with zeros */ + spec->zero_flag = (! spec->lj + && ((spec->zero_flag && ! spec->have_prec) + || (spec->fw == 0 && spec->have_prec))); + + (void) mpfr_get_z(_mpzval, mf, MPFR_RNDZ); /* convert to GMP integer */ + mpfmt_spec = spec->have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC; + zi = _mpzval; + goto fmt0; + + +#if 0 +out_of_range: + /* out of range - emergency use of %g format */ + if (do_lint) + lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), + (double) tmpval, cs1); + cs1 = 'g'; + goto fmt1; + +#endif + + case 'F': +#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 + cs1 = 'f'; + /* FALL THROUGH */ +#endif + case 'g': + case 'G': + case 'e': + case 'f': + case 'E': + if (is_mpfp_float(arg)) { + mf = arg->qnumbr; + mpfmt_spec = MP_FLOAT; + } else { + /* arbitrary-precision integer, convert to MPFR float */ + assert(mf == NULL); + mf = mpz2mpfr(arg->qnumbr, _mpfrval); + mpfmt_spec = MP_FLOAT; + } +fmt1: + if (! spec->have_prec) + spec->prec = DEFAULT_G_PRECISION; + +fmt0: + chksize(outb, spec->fw + spec->prec + 11); /* 11 == slop */ + cp = CPBUF; /* XXX --- using the temporary prepend-buffer and + * we know it has enough room (>=11). + */ + *cp++ = '%'; + if (spec->lj) + *cp++ = '-'; + if (spec->signchar) + *cp++ = spec->signchar; + if (spec->alt) + *cp++ = '#'; + if (spec->zero_flag) + *cp++ = '0'; + if (spec->quote_flag) + *cp++ = '\''; + +#if defined(LC_NUMERIC) + if (spec->quote_flag && ! use_lc_numeric) + setlocale(LC_NUMERIC, ""); +#endif + + switch (mpfmt_spec) { + case MP_INT_WITH_PREC: + sprintf(cp, "*.*Z%c", cs1); + while ((nc = mpfr_snprintf(buf_end(outb), buf_space(outb), CPBUF, + (int) spec->fw, (int) spec->prec, zi)) >= buf_space(outb)) + chksize(outb, nc + 1); + break; + case MP_INT_WITHOUT_PREC: + sprintf(cp, "*Z%c", cs1); + while ((nc = mpfr_snprintf(buf_end(outb), buf_space(outb), CPBUF, + (int) spec->fw, zi)) >= buf_space(outb)) + chksize(outb, nc + 1); + break; + case MP_FLOAT: + sprintf(cp, "*.*R*%c", cs1); + while ((nc = mpfr_snprintf(buf_end(outb), buf_space(outb), CPBUF, + (int) spec->fw, (int) spec->prec, ROUND_MODE, mf)) >= buf_space(outb)) + chksize(outb, nc + 1); + break; + default: + cant_happen(); + } + +#if defined(LC_NUMERIC) + if (spec->quote_flag && ! use_lc_numeric) + setlocale(LC_NUMERIC, "C"); +#endif + + buf_adjust(outb, nc); /* adjust data and free space in output buffer */ + return 0; + + default: + cant_happen(); + } + + return -1; + +#undef CP +#undef CEND +#undef CPBUF } -void -mpfr_unset(NODE *n) +#else + +static bool mpfp_init(bltin_t **bltins); + +numbr_handler_t mpfp_hndlr = { + mpfp_init, + NULL, + NULL, +}; + +/* mpfp_init --- set up MPFR related variables */ + +static bool +mpfp_init(bltin_t **bltins) { - /* dummy function */ + warning(_("this version of gawk does not support arbitrary-precision numbers")); + *bltins = NULL; + return false; } + #endif @@ -26,6 +26,7 @@ #include "awk.h" +extern NODE *format_tree(const char *, size_t, NODE **, long); /* format.c */ extern FILE *output_fp; int sourceline = 0; char *source = NULL; @@ -70,26 +71,16 @@ err(bool isfatal, const char *s, const char *emsg, va_list argp) (void) fprintf(stderr, "%d: ", sourceline); } -#ifdef HAVE_MPFR - if (FNR_node && is_mpg_number(FNR_node->var_value)) { + if (FNR_node != NULL && (FNR_node->var_value->flags & (NUMBER|NUMCUR)) != 0) { NODE *val; - val = mpg_update_var(FNR_node); - assert((val->flags & MPZN) != 0); - if (mpz_sgn(val->mpg_i) > 0) { + val = numbr_hndlr->update_numvar(FNR_node); + if (sgn_number(val) > 0) { /* positive nonzero number */ file = FILENAME_node->var_value->stptr; (void) putc('(', stderr); if (file) (void) fprintf(stderr, "FILENAME=%s ", file); - (void) mpfr_fprintf(stderr, "FNR=%Zd) ", val->mpg_i); + fprintf(stderr, "FNR=%s) ", fmt_number("%d", val)); } - } else -#endif - if (FNR > 0) { - file = FILENAME_node->var_value->stptr; - (void) putc('(', stderr); - if (file) - (void) fprintf(stderr, "FILENAME=%s ", file); - (void) fprintf(stderr, "FNR=%ld) ", FNR); } (void) fprintf(stderr, "%s", s); @@ -105,6 +96,41 @@ err(bool isfatal, const char *s, const char *emsg, va_list argp) } } + +/* + * fmt_number --- format a number node for use in error messages. + * + * N.B: format is awk printf format. MUST NOT generate warning + * in format_tree(). "%ld" is BAD, "%d" is OK! + */ + +const char * +fmt_number(const char *format, const NODE *n) +{ + NODE *dummy[2], *r, *tmp; + static char *num_str; + extern bool fmt_ok(const char *p); + + /* FIXME: %d etc -- assert(fmt_ok(format) == true); */ + assert((n->flags & (NUMBER|NUMCUR)) != 0); + + /* copy number so not to change state of the original including flags */ + tmp = numbr_hndlr->gawk_copy_number(n); + if (num_str != NULL) + efree(num_str); + + /* create dummy node for sole use of format_tree */ + dummy[1] = tmp; + r = format_tree(format, strlen(format), dummy, 2); + assert(r != NULL); + num_str = r->stptr; + num_str[r->stlen] = '\0'; + freenode(r); + unref(tmp); + return num_str; +} + + /* msg --- take a varargs error message and print it */ void @@ -25,240 +25,19 @@ */ #include "awk.h" -#include "math.h" -#include "floatmagic.h" /* definition of isnan */ -static int is_ieee_magic_val(const char *val); -static NODE *r_make_number(double x); -static AWKNUM get_ieee_magic_val(const char *val); -extern NODE **fmt_list; /* declared in eval.c */ - -NODE *(*make_number)(double) = r_make_number; -NODE *(*str2number)(NODE *) = r_force_number; -NODE *(*format_val)(const char *, int, NODE *) = r_format_val; -int (*cmp_numbers)(const NODE *, const NODE *) = cmp_awknums; - -/* force_number --- force a value to be numeric */ - -NODE * -r_force_number(NODE *n) -{ - char *cp; - char *cpend; - char save; - char *ptr; - unsigned int newflags; - extern double strtod(); - - if ((n->flags & NUMCUR) != 0) - return n; - - /* all the conditionals are an attempt to avoid the expensive strtod */ - - /* Note: only set NUMCUR if we actually convert some digits */ - - n->numbr = 0.0; - - if (n->stlen == 0) { - return n; - } - - cp = n->stptr; - /* - * 2/2007: - * POSIX, by way of severe language lawyering, seems to - * allow things like "inf" and "nan" to mean something. - * So if do_posix, the user gets what he deserves. - * This also allows hexadecimal floating point. Ugh. - */ - if (! do_posix) { - if (is_alpha((unsigned char) *cp)) { - return n; - } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) { - if ((n->flags & MAYBE_NUM) != 0) - n->flags &= ~MAYBE_NUM; - n->flags |= NUMBER|NUMCUR; - n->numbr = get_ieee_magic_val(n->stptr); - - return n; - } - /* else - fall through */ - } - /* else not POSIX, so - fall through */ - - cpend = cp + n->stlen; - while (cp < cpend && isspace((unsigned char) *cp)) - cp++; - - if ( cp == cpend /* only spaces, or */ - || (! do_posix /* not POSIXLY paranoid and */ - && (is_alpha((unsigned char) *cp) /* letter, or */ - /* CANNOT do non-decimal and saw 0x */ - || (! do_non_decimal_data && cp[0] == '0' - && (cp[1] == 'x' || cp[1] == 'X'))))) { - return n; - } - - if ((n->flags & MAYBE_NUM) != 0) { - newflags = NUMBER; - n->flags &= ~MAYBE_NUM; - } else - newflags = 0; - - if (cpend - cp == 1) { /* only one character */ - if (isdigit((unsigned char) *cp)) { /* it's a digit! */ - n->numbr = (AWKNUM)(*cp - '0'); - n->flags |= newflags; - n->flags |= NUMCUR; - if (cp == n->stptr) /* no leading spaces */ - n->flags |= NUMINT; - } - return n; - } - - if (do_non_decimal_data) { /* main.c assures false if do_posix */ - errno = 0; - if (! do_traditional && get_numbase(cp, true) != 10) { - n->numbr = nondec2awknum(cp, cpend - cp); - n->flags |= NUMCUR; - ptr = cpend; - goto finish; - } - } - - errno = 0; - save = *cpend; - *cpend = '\0'; - n->numbr = (AWKNUM) strtod((const char *) cp, &ptr); - - /* POSIX says trailing space is OK for NUMBER */ - while (isspace((unsigned char) *ptr)) - ptr++; - *cpend = save; -finish: - if (errno == 0 && ptr == cpend) { - n->flags |= newflags; - n->flags |= NUMCUR; - } else { - errno = 0; - } - - return n; -} - - -/* - * The following lookup table is used as an optimization in force_string; - * (more complicated) variations on this theme didn't seem to pay off, but - * systematic testing might be in order at some point. - */ -static const char *values[] = { - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", -}; -#define NVAL (sizeof(values)/sizeof(values[0])) - -/* r_format_val --- format a numeric value based on format */ - -NODE * -r_format_val(const char *format, int index, NODE *s) -{ - char buf[BUFSIZ]; - char *sp = buf; - double val; - - /* - * 2/2007: Simplify our lives here. Instead of worrying about - * whether or not the value will fit into a long just so we - * can use sprintf("%ld", val) on it, always format it ourselves. - * The only thing to worry about is that integral values always - * format as integers. %.0f does that very well. - * - * 6/2008: Would that things were so simple. Always using %.0f - * imposes a notable performance penalty for applications that - * do a lot of conversion of integers to strings. So, we reinstate - * the old code, but use %.0f for integral values that are outside - * the range of a long. This seems a reasonable compromise. - * - * 12/2009: Use <= and >= in the comparisons with LONG_xxx instead of - * < and > so that things work correctly on systems with 64 bit integers. - */ - - /* not an integral value, or out of range */ - if ((val = double_to_int(s->numbr)) != s->numbr - || val <= LONG_MIN || val >= LONG_MAX - ) { - /* - * Once upon a time, we just blindly did this: - * sprintf(sp, format, s->numbr); - * s->stlen = strlen(sp); - * s->stfmt = (char) index; - * but that's no good if, e.g., OFMT is %s. So we punt, - * and just always format the value ourselves. - */ - - NODE *dummy[2], *r; - unsigned int oflags; - - /* create dummy node for a sole use of format_tree */ - dummy[1] = s; - oflags = s->flags; - - if (val == s->numbr) { - /* integral value, but outside range of %ld, use %.0f */ - r = format_tree("%.0f", 4, dummy, 2); - s->stfmt = -1; - } else { - r = format_tree(format, fmt_list[index]->stlen, dummy, 2); - assert(r != NULL); - s->stfmt = (char) index; - } - s->flags = oflags; - s->stlen = r->stlen; - if ((s->flags & STRCUR) != 0) - efree(s->stptr); - s->stptr = r->stptr; - freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */ - - goto no_malloc; - } else { - /* - * integral value; force conversion to long only once. - */ - long num = (long) val; - - if (num < NVAL && num >= 0) { - sp = (char *) values[num]; - s->stlen = 1; - } else { - (void) sprintf(sp, "%ld", num); - s->stlen = strlen(sp); - } - s->stfmt = -1; - if ((s->flags & INTIND) != 0) { - s->flags &= ~(INTIND|NUMBER); - s->flags |= STRING; - } - } - if (s->stptr != NULL) - efree(s->stptr); - emalloc(s->stptr, char *, s->stlen + 2, "format_val"); - memcpy(s->stptr, sp, s->stlen + 1); -no_malloc: - s->flags |= STRCUR; - free_wstr(s); - return s; -} +int (*format_number_printf)(NODE *, struct format_spec *, struct print_fmt_buf *); +NODE *(*str2node)(char *, char **, int, bool); +NODE *(*make_number)(AWKNUM); +NODE *(*str2number)(NODE *); +NODE *(*format_val)(const char *, int, NODE *); +int (*cmp_numbers)(const NODE *, const NODE *); +void (*free_number)(NODE *); +unsigned long (*get_number_ui)(const NODE *); +long (*get_number_si)(const NODE *); +AWKNUM (*get_number_d)(const NODE *); +uintmax_t (*get_number_uj)(const NODE *); +int (*sgn_number)(const NODE *); /* r_dupnode --- duplicate a node */ @@ -309,53 +88,6 @@ r_dupnode(NODE *n) return r; } -/* r_make_number --- allocate a node with defined number */ - -static NODE * -r_make_number(double x) -{ - NODE *r; - getnode(r); - r->type = Node_val; - r->numbr = x; - r->flags = MALLOC|NUMBER|NUMCUR; - r->valref = 1; - r->stptr = NULL; - r->stlen = 0; -#if MBS_SUPPORT - r->wstptr = NULL; - r->wstlen = 0; -#endif /* defined MBS_SUPPORT */ - return r; -} - -/* cmp_awknums --- compare two AWKNUMs */ - -int -cmp_awknums(const NODE *t1, const NODE *t2) -{ - /* - * This routine is also used to sort numeric array indices or values. - * For the purposes of sorting, NaN is considered greater than - * any other value, and all NaN values are considered equivalent and equal. - * This isn't in compliance with IEEE standard, but compliance w.r.t. NaN - * comparison at the awk level is a different issue, and needs to be dealt - * with in the interpreter for each opcode seperately. - */ - - if (isnan(t1->numbr)) - return ! isnan(t2->numbr); - if (isnan(t2->numbr)) - return -1; - /* don't subtract, in case one or both are infinite */ - if (t1->numbr == t2->numbr) - return 0; - if (t1->numbr < t2->numbr) - return -1; - return 1; -} - - /* make_str_node --- make a string node */ NODE * @@ -456,7 +188,8 @@ r_unref(NODE *tmp) efree(tmp->stptr); #endif - mpfr_unset(tmp); + if (free_number && (tmp->flags & (NUMBER|NUMCUR)) != 0) + free_number(tmp); free_wstr(tmp); freenode(tmp); @@ -892,50 +625,6 @@ out: ; } #endif /* MBS_SUPPORT */ -/* is_ieee_magic_val --- return true for +inf, -inf, +nan, -nan */ - -static int -is_ieee_magic_val(const char *val) -{ - /* - * Avoid strncasecmp: it mishandles ASCII bytes in some locales. - * Assume the length is 4, as the caller checks this. - */ - return ( (val[0] == '+' || val[0] == '-') - && ( ( (val[1] == 'i' || val[1] == 'I') - && (val[2] == 'n' || val[2] == 'N') - && (val[3] == 'f' || val[3] == 'F')) - || ( (val[1] == 'n' || val[1] == 'N') - && (val[2] == 'a' || val[2] == 'A') - && (val[3] == 'n' || val[3] == 'N')))); -} - -/* get_ieee_magic_val --- return magic value for string */ - -static AWKNUM -get_ieee_magic_val(const char *val) -{ - static bool first = true; - static AWKNUM inf; - static AWKNUM nan; - - char *ptr; - AWKNUM v = strtod(val, &ptr); - - if (val == ptr) { /* Older strtod implementations don't support inf or nan. */ - if (first) { - first = false; - nan = sqrt(-1.0); - inf = -log(0.0); - } - - v = ((val[1] == 'i' || val[1] == 'I') ? inf : nan); - if (val[0] == '-') - v = -v; - } - - return v; -} #if MBS_SUPPORT wint_t btowc_cache[256]; @@ -335,27 +335,6 @@ cleanup: pc = pc->target_jmp; break; - case Op_plus_i: - case Op_minus_i: - case Op_times_i: - case Op_exp_i: - case Op_quotient_i: - case Op_mod_i: - m = pc->memory; - t1 = pp_pop(); - if (prec_level(pc->opcode) > prec_level(t1->type) - && is_binary(t1->type)) /* (a - b) * 1 */ - pp_parenthesize(t1); - if ((m->flags & NUMBER) != 0) - tmp = pp_number(m); - else - tmp = pp_string(m->stptr, m->stlen, '"'); - str = pp_group3(t1->pp_str, op2str(pc->opcode), tmp); - efree(tmp); - pp_free(t1); - pp_push(pc->opcode, str, CAN_FREE); - break; - case Op_plus: case Op_minus: case Op_times: @@ -393,6 +372,7 @@ cleanup: case Op_field_spec: case Op_field_spec_lhs: case Op_unary_minus: + case Op_unary_plus: case Op_not: t1 = pp_pop(); if (is_binary(t1->type)) @@ -1007,7 +987,6 @@ prec_level(int type) return 14; case Op_exp: - case Op_exp_i: return 13; case Op_preincrement: @@ -1017,21 +996,17 @@ prec_level(int type) return 12; case Op_unary_minus: + case Op_unary_plus: case Op_not: return 11; case Op_times: - case Op_times_i: case Op_quotient: - case Op_quotient_i: case Op_mod: - case Op_mod_i: return 10; case Op_plus: - case Op_plus_i: case Op_minus: - case Op_minus_i: return 9; case Op_concat: @@ -1127,12 +1102,6 @@ is_binary(int type) case Op_mod: case Op_plus: case Op_minus: - case Op_exp_i: - case Op_times_i: - case Op_quotient_i: - case Op_mod_i: - case Op_plus_i: - case Op_minus_i: case Op_concat: case Op_assign_concat: case Op_match: @@ -1277,20 +1246,9 @@ pp_string(const char *in_str, size_t len, int delim) char * pp_number(NODE *n) { -#define PP_PRECISION 6 - char *str; - - emalloc(str, char *, PP_PRECISION + 10, "pp_number"); -#ifdef HAVE_MPFR - if (is_mpg_float(n)) - mpfr_sprintf(str, "%0.*R*g", PP_PRECISION, ROUND_MODE, n->mpg_numbr); - else if (is_mpg_integer(n)) - mpfr_sprintf(str, "%Zd", n->mpg_i); - else -#endif - sprintf(str, "%0.*g", PP_PRECISION, n->numbr); - return str; -#undef PP_PRECISION + const char *str; + str = fmt_number("%0.6g", n); + return estrdup(str, strlen(str)); } /* pp_node --- pretty format a node */ |