From 180d764e86e0f3b2fd0e2bb613d790adcd5aceb0 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 7 Mar 2005 06:25:36 +0000 Subject: [PATCH] DATA LIST with free-field formats should not have implied decimal places (bug #12035). Also clean up data-in.c a bit. --- src/ChangeLog | 84 ++++++++ src/data-in.c | 382 ++++++++++++--------------------- src/data-in.h | 1 + src/data-list.c | 2 +- src/expressions/ChangeLog | 62 ------ src/expressions/operations.def | 2 +- 6 files changed, 229 insertions(+), 304 deletions(-) delete mode 100644 src/expressions/ChangeLog diff --git a/src/ChangeLog b/src/ChangeLog index 281bcaeb..ffaf3fd3 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,31 @@ +Sun Mar 6 19:52:22 2005 Ben Pfaff + + DATA LIST with free-field formats should not have implied decimal + places (bug #12035). Also clean up data-in.c a bit. + + * data-in.h: (enum) Add DI_IMPLIED_DECIMALS. + + * data-in.c: (apply_implied_decimals) New function. + (parse_numeric) Don't adjust exponent if DI_IMPLIED_DECIMALS not + set. Also, get rid of gotos. + (parse_Z) Use apply_implied_decimals() if the field doesn't + contain a decimal point. + (parse_N) Use apply_implied_decimals(). + (parse_IB) Ditto. + (parse_PIB) Ditto. + (parse_P) Ditto. + (parse_PK) Ditto. + (to_roman) Removed. + (parse_enum) New function. + (macro CHAR_IS_ROMAN) Removed. + (macro ROMAN_VALUE) Removed. + (parse_month) Use parse_enum(). + (parse_weekday) Use parse_enum(). + (parse_DATETIME) Use long for weekday. + + * data-list.c: (read_from_data_list_fixed) Use + DI_IMPLIED_DECIMALS. + Sun Mar 6 17:07:20 2005 Ben Pfaff When the lexer sees something like `-5' in the input, it has to @@ -25,6 +53,62 @@ Sun Mar 6 17:07:20 2005 Ben Pfaff * lexer.h: (enum) Add T_POS_NUM, T_NEG_NUM. Remove T_NUM. +Sun Mar 6 22:09:20 2005 Ben Pfaff + + * operations.def: (NUMBER) Use DI_IMPLIED_DECIMALS. + +Sun Mar 6 19:33:24 2005 Ben Pfaff + + * operations.def: (VEC_ELEM_NUM) Treat user-missing values as + system-missing. + + * parse.c: (parse_vector_element) Fix order of arguments in call + to expr_allocate_binary(). + +Sun Mar 6 17:51:05 2005 Ben Pfaff + + * optimize.c: (optimize_tree) Fix optimization bug for x**2. + + * parse.c: (type_coercion_core) Set *node to NULL on failure, as + indicated by function comment. + (parse_binary_operators) Always return NULL on type_coercion() + failure. Should have been doing this anyway, but bug in + type_coercion_core() filtered through. + (parse_add) Fix typo in user message. + (parse_primary) Understand T_NEG_NUM and T_POS_NUM. + +Sun Mar 6 10:47:13 2005 Ben Pfaff + + * operations.def: Add VALUE function. + + * parse.c: (parse_function) Need an unary composite node for + variables in A TO B, not a variable node. Use + allocate_unary_variable(). + (parse_primary) Use allocate_unary_variable(). + (allocate_unary_variable) New function. + +Thu Mar 3 23:53:32 2005 Ben Pfaff + + * PSPP_expressions.pm: Renamed it back to generate.pl but fixed + the real problem that was preventing the build from a separate + directory. I liked it my way better ;-) + +Thu Mar 3 23:17:51 2005 Ben Pfaff + + * parse.c: (expr_parse) Fix parameter type. Thanks to John + Darrington for reporting this bug. + +Thu Mar 3 22:10:25 WST 2005 John Darrington + + * Makefile.am evaluate.h.pl evaluate.inc.pl operations.h.pl + optimize.inc.pl parse.inc.p: + + Renamed generate.pl to PSPP_expressions.pm and adjusted *.pl + to suit. + + Fixed everything so that it can be built from an arbitrary + directory. + Thu Mar 3 22:08:35 WST 2005 John Darrington * Makefile.am : Fixed up CLEANFILES target. diff --git a/src/data-in.c b/src/data-in.c index 97ad2da5..4b16ef2d 100644 --- a/src/data-in.c +++ b/src/data-in.c @@ -81,6 +81,8 @@ dls_error (const struct data_in *i, const char *format, ...) vdls_error (i, format, args); va_end (args); } + +/* Parsing utility functions. */ /* Excludes leading and trailing whitespace from I by adjusting pointers. */ @@ -101,6 +103,15 @@ have_char (struct data_in *i) { return i->s < i->e; } + +/* If implied decimal places are enabled, apply them to + I->v->f. */ +static void +apply_implied_decimals (struct data_in *i) +{ + if ((i->flags & DI_IMPLIED_DECIMALS) && i->format.d > 0) + i->v->f /= pow (10., i->format.d); +} /* Format parsers. */ @@ -110,7 +121,7 @@ static int parse_int (struct data_in *i, long *result); static int parse_numeric (struct data_in *i) { - short int sign; /* +1 or -1. */ + int sign; /* +1 or -1. */ double num; /* The number so far. */ int got_dot; /* Found a decimal point. */ @@ -196,7 +207,9 @@ parse_numeric (struct data_in *i) i->v->f = SYSMIS; return 1; } - goto noconv; + dls_error (i, _("Field does not form a valid floating-point constant.")); + i->v->f = SYSMIS; + return 0; } if (have_char (i) @@ -209,11 +222,14 @@ parse_numeric (struct data_in *i) if (isalpha (*i->s)) i->s++; if (!parse_int (i, &exp)) - goto noconv; + { + i->v->f = SYSMIS; + return 0; + } exponent += exp; } - else if (!got_dot) + else if (!got_dot && (i->flags & DI_IMPLIED_DECIMALS)) exponent -= i->format.d; if (type == FMT_PCT && have_char (i) && *i->s == '%') @@ -233,41 +249,32 @@ parse_numeric (struct data_in *i) /* Multiply NUM by 10 to the EXPONENT power, checking for overflow and underflow. */ - if (exponent < 0) { if (-exponent + got_digit > -(DBL_MIN_10_EXP) + 5 - || num < DBL_MIN * pow (10.0, (double) -exponent)) - goto underflow; + || num < DBL_MIN * pow (10.0, (double) -exponent)) + { + dls_error (i, _("Underflow in floating-point constant.")); + i->v->f = 0.0; + return 0; + } + num *= pow (10.0, (double) exponent); } else if (exponent > 0) { if (num > DBL_MAX * pow (10.0, (double) -exponent)) - goto overflow; + { + dls_error (i, _("Overflow in floating-point constant.")); + i->v->f = SYSMIS; + return 0; + } + num *= pow (10.0, (double) exponent); } - i->v->f = sign * num; + i->v->f = sign > 0 ? num : -num; return 1; - -overflow: - /* Return an overflow error. */ - dls_error (i, _("Overflow in floating-point constant.")); - i->v->f = SYSMIS; - return 0; - -underflow: - /* Return an underflow error. */ - dls_error (i, _("Underflow in floating-point constant.")); - i->v->f = 0.0; - return 0; - -noconv: - /* There was no number. */ - dls_error (i, _("Field does not form a valid floating-point constant.")); - i->v->f = SYSMIS; - return 0; } /* Returns the integer value of hex digit C. */ @@ -298,8 +305,7 @@ parse_N (struct data_in *i) i->v->f = i->v->f * 10.0 + *cp - '0'; } - if (i->format.d) - i->v->f /= pow (10.0, i->format.d); + apply_implied_decimals (i); return 1; } @@ -374,6 +380,7 @@ static inline int parse_Z (struct data_in *i) { char buf[64]; + bool got_dot = false; /* Warn user that we suck. */ { @@ -413,8 +420,11 @@ parse_Z (struct data_in *i) char *dp; for (sp = i->s, dp = buf + 1; sp < i->e - 1; sp++, dp++) - if (*sp == '.') - *dp = '.'; + if (*sp == '.') + { + *dp = '.'; + got_dot = true; + } else if ((*sp & 0xf0) == 0xf0 && (*sp & 0xf) < 10) *dp = (*sp & 0xf) + '0'; else @@ -437,7 +447,10 @@ parse_Z (struct data_in *i) return 0; } } - + + if (!got_dot) + apply_implied_decimals (i); + return 1; } @@ -479,8 +492,7 @@ parse_IB (struct data_in *i) if (p[0] & 0x80) i->v->f = -(i->v->f + 1.0); - if (i->format.d) - i->v->f /= pow (10.0, i->format.d); + apply_implied_decimals (i); return 1; } @@ -499,8 +511,7 @@ parse_PIB (struct data_in *i) i->v->f = i->v->f * 256.0 + i->s[j]; #endif - if (i->format.d) - i->v->f /= pow (10.0, i->format.d); + apply_implied_decimals (i); return 1; } @@ -520,8 +531,7 @@ parse_P (struct data_in *i) if ((*cp ^ (*cp >> 1)) & 0x10) i->v->f = -i->v->f; - if (i->format.d) - i->v->f /= pow (10.0, i->format.d); + apply_implied_decimals (i); return 1; } @@ -538,8 +548,7 @@ parse_PK (struct data_in *i) i->v->f = i->v->f * 10 + (*cp & 15); } - if (i->format.d) - i->v->f /= pow (10.0, i->format.d); + apply_implied_decimals (i); return 1; } @@ -714,89 +723,89 @@ parse_date_delimiter (struct data_in *i) return 0; } -/* Formats NUMBER as Roman numerals in ROMAN, or as Arabic numerals if - the Roman expansion would be too long. */ -static void -to_roman (int number, char roman[32]) -{ - int save_number = number; - - struct roman_digit - { - int value; /* Value corresponding to this digit. */ - char name; /* Digit name. */ - }; - - static const struct roman_digit roman_tab[7] = +/* Association between a name and a value. */ +struct enum_name { - {1000, 'M'}, - {500, 'D'}, - {100, 'C'}, - {50, 'L'}, - {10, 'X'}, - {5, 'V'}, - {1, 'I'}, + const char *name; /* Name. */ + bool can_abbreviate; /* True if name may be abbreviated. */ + int value; /* Value associated with name. */ }; - char *cp = roman; - - int i, j; - - assert (32 >= INT_DIGITS + 1); - if (number == 0) - goto arabic; - - if (number < 0) +/* Reads a name from I and sets *OUTPUT to the value associated + with that name. Returns true if successful, false otherwise. */ +static bool +parse_enum (struct data_in *i, const char *what, + const struct enum_name *enum_names, + long *output) +{ + const char *name; + size_t length; + const struct enum_name *ep; + + /* Consume alphabetic characters. */ + name = i->s; + length = 0; + while (have_char (i) && isalpha (*i->s)) { - *cp++ = '-'; - number = -number; + length++; + i->s++; } - - for (i = 0; i < 7; i++) + if (length == 0) { - int digit = roman_tab[i].value; - while (number >= digit) - { - number -= digit; - if (cp > &roman[30]) - goto arabic; - *cp++ = roman_tab[i].name; - } - - for (j = i + 1; j < 7; j++) - { - if (i == 4 && j == 5) /* VX is not a shortened form of V. */ - break; - - digit = roman_tab[i].value - roman_tab[j].value; - while (number >= digit) - { - number -= digit; - if (cp > &roman[29]) - goto arabic; - *cp++ = roman_tab[j].name; - *cp++ = roman_tab[i].name; - } - } + dls_error (i, _("Parse error at `%c' expecting %s."), *i->s, what); + return 0; } - *cp = 0; - return; - -arabic: - sprintf (roman, "%d", save_number); -} -/* Returns true if C is a (lowercase) roman numeral. */ -#define CHAR_IS_ROMAN(C) \ - ((C) == 'x' || (C) == 'v' || (C) == 'i') + for (ep = enum_names; ep->name != NULL; ep++) + if ((ep->can_abbreviate + && lex_id_match_len (ep->name, strlen (ep->name), name, length)) + || (!ep->can_abbreviate && length == strlen (ep->name) + && !memcmp (name, ep->name, length))) + { + *output = ep->value; + return 1; + } -/* Returns the value of a single (lowercase) roman numeral. */ -#define ROMAN_VALUE(C) \ - ((C) == 'x' ? 10 : ((C) == 'v' ? 5 : 1)) + dls_error (i, _("Unknown %s `%.*s'."), what, (int) length, name); + return 0; +} static int parse_month (struct data_in *i, long *month) { + static const struct enum_name month_names[] = + { + {"january", true, 1}, + {"february", true, 2}, + {"march", true, 3}, + {"april", true, 4}, + {"may", true, 5}, + {"june", true, 6}, + {"july", true, 7}, + {"august", true, 8}, + {"september", true, 9}, + {"october", true, 10}, + {"november", true, 11}, + {"december", true, 12}, + + {"i", false, 1}, + {"ii", false, 2}, + {"iii", false, 3}, + {"iv", false, 4}, + {"iiii", false, 4}, + {"v", false, 5}, + {"vi", false, 6}, + {"vii", false, 7}, + {"viii", false, 8}, + {"ix", false, 9}, + {"viiii", false, 9}, + {"x", false, 10}, + {"xi", false, 11}, + {"xii", false, 12}, + + {NULL, false, 0}, + }; + if (!force_have_char (i)) return 0; @@ -810,85 +819,8 @@ parse_month (struct data_in *i, long *month) dls_error (i, _("Month (%ld) must be between 1 and 12."), *month); return 0; } - - if (CHAR_IS_ROMAN (tolower (*i->s))) - { - int last = ROMAN_VALUE (tolower (*i->s)); - - *month = 0; - for (;;) - { - int value; - - i->s++; - if (!have_char || !CHAR_IS_ROMAN (tolower (*i->s))) - { - if (last != INT_MAX) - *month += last; - break; - } - - value = ROMAN_VALUE (tolower (*i->s)); - if (last == INT_MAX) - *month += value; - else if (value > last) - { - *month += value - last; - last = INT_MAX; - } - else - { - *month += last; - last = value; - } - } - - if (*month < 1 || *month > 12) - { - char buf[32]; - - to_roman (*month, buf); - dls_error (i, _("Month (%s) must be between I and XII."), buf); - return 0; - } - - return 1; - } - - { - static const char *months[12] = - { - "january", "february", "march", "april", "may", "june", - "july", "august", "september", "october", "november", "december", - }; - - char month_buf[32]; - char *mp; - - int j; - - for (mp = month_buf; - have_char (i) && isalpha (*i->s) && mp < &month_buf[31]; - i->s++) - *mp++ = tolower (*i->s); - *mp = '\0'; - - if (have_char (i) && isalpha (*i->s)) - { - dls_error (i, _("Month name (%s...) is too long."), month_buf); - return 0; - } - - for (j = 0; j < 12; j++) - if (lex_id_match (months[j], month_buf)) - { - *month = j + 1; - return 1; - } - - dls_error (i, _("Bad month name (%s)."), month_buf); - return 0; - } + else + return parse_enum (i, _("month"), month_names, month); } static int @@ -1093,59 +1025,29 @@ parse_hour24 (struct data_in *i, long *hour24) static int -parse_weekday (struct data_in *i, int *weekday) +parse_weekday (struct data_in *i, long *weekday) { - /* PORTME */ - #define TUPLE(A,B) \ - (((A) << 8) + (B)) - - if (i->s + 1 >= i->e) - { - dls_error (i, _("Day of the week expected in date value.")); - return 0; - } - - switch (TUPLE (tolower (i->s[0]), tolower (i->s[1]))) + static const struct enum_name weekday_names[] = { - case TUPLE ('s', 'u'): - *weekday = 1; - break; - - case TUPLE ('m', 'o'): - *weekday = 2; - break; - - case TUPLE ('t', 'u'): - *weekday = 3; - break; - - case TUPLE ('w', 'e'): - *weekday = 4; - break; - - case TUPLE ('t', 'h'): - *weekday = 5; - break; - - case TUPLE ('f', 'r'): - *weekday = 6; - break; - - case TUPLE ('s', 'a'): - *weekday = 7; - break; - - default: - dls_error (i, _("Day of the week expected in date value.")); - return 0; - } - - while (have_char (i) && isalpha (*i->s)) - i->s++; - - return 1; + {"sunday", true, 1}, + {"su", true, 1}, + {"monday", true, 2}, + {"mo", true, 2}, + {"tuesday", true, 3}, + {"tu", true, 3}, + {"wednesday", true, 4}, + {"we", true, 4}, + {"thursday", true, 5}, + {"th", true, 5}, + {"friday", true, 6}, + {"fr", true, 6}, + {"saturday", true, 7}, + {"sa", true, 7}, + + {NULL, false, 0}, + }; - #undef TUPLE + return parse_enum (i, _("weekday"), weekday_names, weekday); } static int @@ -1418,7 +1320,7 @@ parse_DATETIME (struct data_in *i) static int parse_WKDAY (struct data_in *i) { - int weekday; + long weekday; if (!parse_leader (i) || !parse_weekday (i, &weekday) diff --git a/src/data-in.h b/src/data-in.h index 450e10d2..8720fbe1 100644 --- a/src/data-in.h +++ b/src/data-in.h @@ -27,6 +27,7 @@ enum { DI_IGNORE_ERROR = 01, /* Don't report errors to the user. */ + DI_IMPLIED_DECIMALS = 02 /* Insert decimals if no '.' in input. */ }; /* Information about parsing one data field. */ diff --git a/src/data-list.c b/src/data-list.c index 9d935609..9fc3f9de 100644 --- a/src/data-list.c +++ b/src/data-list.c @@ -1113,7 +1113,7 @@ read_from_data_list_fixed (const struct data_list_pgm *dls, data_in_finite_line (&di, ls_c_str (&line), ls_length (&line), var_spec->fc, var_spec->lc); di.v = case_data_rw (c, var_spec->fv); - di.flags = 0; + di.flags = DI_IMPLIED_DECIMALS; di.f1 = var_spec->fc; di.format = var_spec->input; diff --git a/src/expressions/ChangeLog b/src/expressions/ChangeLog deleted file mode 100644 index 9de71daf..00000000 --- a/src/expressions/ChangeLog +++ /dev/null @@ -1,62 +0,0 @@ -Sun Mar 6 19:33:24 2005 Ben Pfaff - - * operations.def: (VEC_ELEM_NUM) Treat user-missing values as - system-missing. - - * parse.c: (parse_vector_element) Fix order of arguments in call - to expr_allocate_binary(). - -Sun Mar 6 17:51:05 2005 Ben Pfaff - - * optimize.c: (optimize_tree) Fix optimization bug for x**2. - - * parse.c: (type_coercion_core) Set *node to NULL on failure, as - indicated by function comment. - (parse_binary_operators) Always return NULL on type_coercion() - failure. Should have been doing this anyway, but bug in - type_coercion_core() filtered through. - (parse_add) Fix typo in user message. - (parse_primary) Understand T_NEG_NUM and T_POS_NUM. - -Sun Mar 6 10:47:13 2005 Ben Pfaff - - * operations.def: Add VALUE function. - - * parse.c: (parse_function) Need an unary composite node for - variables in A TO B, not a variable node. Use - allocate_unary_variable(). - (parse_primary) Use allocate_unary_variable(). - (allocate_unary_variable) New function. - -Thu Mar 3 23:53:32 2005 Ben Pfaff - - * PSPP_expressions.pm: Renamed it back to generate.pl but fixed - the real problem that was preventing the build from a separate - directory. I liked it my way better ;-) - -Thu Mar 3 23:17:51 2005 Ben Pfaff - - * parse.c: (expr_parse) Fix parameter type. Thanks to John - Darrington for reporting this bug. - -Thu Mar 3 22:10:25 WST 2005 John Darrington - - * Makefile.am evaluate.h.pl evaluate.inc.pl operations.h.pl - optimize.inc.pl parse.inc.p: - - Renamed generate.pl to PSPP_expressions.pm and adjusted *.pl - to suit. - - Fixed everything so that it can be built from an arbitrary - directory. - - -Mon Feb 28 23:52:21 2005 Ben Pfaff - - * New directory. - ----------------------------------------------------------------------- -Local Variables: -mode: change-log -version-control: never -End: diff --git a/src/expressions/operations.def b/src/expressions/operations.def index 8165b740..e1998266 100644 --- a/src/expressions/operations.def +++ b/src/expressions/operations.def @@ -551,7 +551,7 @@ function NUMBER (string s, ni_format f) union value out; di.s = s.string; di.v = &out; - di.flags = 0; + di.flags = DI_IMPLIED_DECIMALS; di.f1 = 1; di.format = *f; di.e = s.string + min (s.length, di.format.w); -- 2.30.2