/* --- @parse_floating@ --- *
*
* Arguments: @double *x_out@ = where to put the result
+ * @const char *q_out@ = where to leave end pointer, or null
* @const char *p@ = string to parse
* @const struct tvec_floatinfo *fi@ = floating-point info
* @struct tvec_state *tv@ = test vector state
* Returns: Zero on success, @-1@ on error.
*
* Use: Parse a floating-point number from a string. Reports any
- * necessary errors.
+ * necessary errors. If @q_out@ is not null then trailing
+ * material is permitted and a pointer to it is left in
+ * @*q_out@; this will be null if there is no trailing material.
*/
-static int parse_floating(double *x_out, const char *p,
+static int parse_floating(double *x_out, const char **q_out, const char *p,
const struct tvec_floatinfo *fi,
struct tvec_state *tv)
{
double x;
int olderr, rc;
+ if (q_out) *q_out = 0;
+
/* Check for special tokens. */
if (STRCMP(p, ==, "#nan")) {
#ifdef NAN
/* Parse the number using the system parser. */
olderr = errno; errno = 0;
x = strtod(p, &q);
- if (*q) {
- tvec_syntax(tv, *q, "end-of-line");
- rc = -1; goto end;
- }
+ if (!*q) /* nothing to do */;
+ else if (q_out) *q_out = q;
+ else { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; }
if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
- tvec_error(tv, "invalid floating-point number `%s': %s",
- p, strerror(errno));
+ tvec_error(tv, "invalid floating-point number `%.*s': %s",
+ (int)(q - p), p, strerror(errno));
rc = -1; goto end;
}
errno = olderr;
/*----- Floating-point type -----------------------------------------------*/
-/* --- @float_int@ --- *
+/* --- @int_float@ --- *
*
* Arguments: @union tvec_regval *rv@ = register value
* @const struct tvec_regdef *rd@ = register definition
if (tvec_readword(tv, &d, ";", "floating-point number"))
{ rc = -1; goto end; }
- if (parse_floating(&rv->f, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
+ if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv))
+ { rc = -1; goto end; }
if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
rc = 0;
end:
file, lno, expr));
}
+/*----- Durations ---------------------------------------------------------*/
+
+/* A duration is a floating-point number of seconds. Initialization and
+ * teardown, equality comparison, and serialization are as for floating-point
+ * values.
+ */
+
+static const struct duration_unit {
+ const char *unit;
+ double scale;
+ unsigned f;
+#define DUF_PREFER 1u
+} duration_units[] = {
+ { "Ys", 1e+24, 0 },
+ { "Zs", 1e+21, 0 },
+ { "Es", 1e+18, 0 },
+ { "Ps", 1e+15, 0 },
+ { "Ts", 1e+12, 0 },
+ { "Gs", 1e+9, 0 },
+ { "Ms", 1e+6, 0 },
+ { "ks", 1e+3, 0 },
+ { "hs", 1e+2, 0 },
+ { "das", 1e+1, 0 },
+
+ { "yr", 31557600.0, DUF_PREFER },
+ { "y", 31557600.0, 0 },
+ { "day", 86400.0, DUF_PREFER },
+ { "dy", 86400.0, 0 },
+ { "d", 86400.0, 0 },
+ { "hr", 3600.0, DUF_PREFER },
+ { "hour", 3600.0, 0 },
+ { "h", 3600.0, 0 },
+ { "min", 60.0, DUF_PREFER },
+ { "m", 60.0, 0 },
+
+ { "s", 1.0, DUF_PREFER },
+ { "sec", 1.0, 0 },
+
+ { "ds", 1e-1, 0 },
+ { "cs", 1e-2, 0 },
+ { "ms", 1e-3, DUF_PREFER },
+ { "µs", 1e-6, DUF_PREFER },
+ { "ns", 1e-9, DUF_PREFER },
+ { "ps", 1e-12, DUF_PREFER },
+ { "fs", 1e-15, DUF_PREFER },
+ { "as", 1e-18, DUF_PREFER },
+ { "zs", 1e-21, DUF_PREFER },
+ { "ys", 1e-24, DUF_PREFER },
+
+ { 0 }
+};
+
+/* --- @parse_duration@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ * @const struct tvec_regdef *rd@ = register definition
+ * @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: Parse a register value from an input file.
+ *
+ * Duration values are finite nonnegative floating-point
+ * numbers in @strtod@ syntax, optionally followed by a unit .
+ */
+
+static int parse_duration(union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ struct tvec_state *tv)
+{
+ const struct duration_unit *u;
+ const char *q;
+ dstr d = DSTR_INIT; size_t pos;
+ double t;
+ int rc;
+
+ if (tvec_readword(tv, &d, ";", "duration")) { rc = -1; goto end; }
+ if (parse_floating(&t, &q, d.buf,
+ rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv))
+ { rc = -1; goto end; }
+
+ if (!q) {
+ tvec_skipspc(tv); pos = d.len;
+ if (!tvec_readword(tv, &d, ";", 0)) q = d.buf + pos + 1;
+ }
+
+ if (q) {
+ for (u = duration_units; u->unit; u++)
+ if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; }
+ rc = tvec_syntax(tv, *q, "end-of-line"); goto end;
+ found_unit:;
+ }
+
+ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
+ rv->f = t; rc = 0;
+end:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+/* --- @dump_duration@ --- *
+ *
+ * Arguments: @const union tvec_regval *rv@ = register value
+ * @const struct tvec_regdef *rd@ = register definition
+ * @unsigned style@ = output style (@TVSF_...@)
+ * @const struct gprintf_ops *gops@, @void *gp@ = format output
+ *
+ * Returns: ---
+ *
+ * Use: Dump a register value to the format output.
+ *
+ * Durations are dumped as a human-palatable scaled value with
+ * unit, and, if compact style is not requested, as a raw number
+ * of seconds at full precision as a comment.
+ */
+
+static void dump_duration(const union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
+{
+ const struct duration_unit *u;
+ double t = rv->f;
+
+ if (!t) u = 0;
+ else {
+ for (u = duration_units; u->scale > t && u[1].unit; u++);
+ t /= u->scale;
+ }
+
+ gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
+ if (!(style&TVSF_COMPACT)) {
+ gprintf(gops, go, "; = ");
+ format_floating(gops, go, rv->f);
+ gprintf(gops, go, " s");
+ }
+}
+
+/* Duration type definition. */
+const struct tvec_regty tvty_duration = {
+ init_float, trivial_release, eq_float,
+ tobuf_float, frombuf_float,
+ parse_duration, dump_duration
+};
+
/*----- Enumerations ------------------------------------------------------*/
/* --- @init_tenum@ --- *
#define LITSTR_FLT "literal floating-point number, " \
"`#-inf', `#+inf', or `#nan'"
#define FOUND_FLT rv->f = a->f;
-#define MISSING_FLT if (parse_floating(&rv->f, d.buf, ei->fi, tv)) \
+#define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
{ rc = -1; goto end; }
#define LITSTR_PTR "`#nil'"