static const char *atom_type_name (atom_type);
static struct expression *finish_expression (struct expr_node *,
struct expression *);
-static bool type_check (const struct expr_node *, enum val_type expected_type);
+static bool type_check (const struct expression *, const struct expr_node *,
+ enum val_type expected_type);
static struct expr_node *allocate_unary_variable (struct expression *,
const struct variable *);
\f
/* Public functions. */
+static struct expr_node *
+parse_expr (struct lexer *lexer, struct expression *e)
+{
+ struct expr_node *n = parse_or (lexer, e);
+ if (n && n->type == OP_VEC_ELEM_NUM_RAW)
+ n->type = OP_VEC_ELEM_NUM;
+ return n;
+}
+
/* Parses an expression of the given TYPE. If DS is nonnull then variables and
vectors within it may be referenced within the expression; otherwise, the
expression must not reference any variables or vectors. Returns the new
assert (val_type_is_valid (type));
struct expression *e = expr_create (ds);
- struct expr_node *n = parse_or (lexer, e);
- if (!n || !type_check (n, type))
+ struct expr_node *n = parse_expr (lexer, e);
+ if (!n || !type_check (e, n, type))
{
expr_free (e);
return NULL;
expr_parse_bool (struct lexer *lexer, struct dataset *ds)
{
struct expression *e = expr_create (ds);
- struct expr_node *n = parse_or (lexer, e);
+ struct expr_node *n = parse_expr (lexer, e);
if (!n)
{
expr_free (e);
atom_type actual_type = expr_node_returns (n);
if (actual_type == OP_number)
- n = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, n);
+ n = expr_allocate_binary (e, OP_EXPR_TO_BOOLEAN, n,
+ expr_allocate_expr_node (e, n));
else if (actual_type != OP_boolean)
{
- msg (SE, _("Type mismatch: expression has %s type, "
+ msg_at (SE, expr_location (e, n),
+ _("Type mismatch: expression has %s type, "
"but a boolean value is required here."),
atom_type_name (actual_type));
expr_free (e);
const char *new_var_name)
{
struct expression *e = expr_create (ds);
- struct expr_node *n = parse_or (lexer, e);
+ struct expr_node *n = parse_expr (lexer, e);
if (!n)
{
expr_free (e);
struct expression *e;
e = expr_create (ds);
- n = parse_or (lexer, e);
+ n = parse_expr (lexer, e);
if (n == NULL)
{
expr_free (e);
{
case OP_number:
case OP_boolean:
+ case OP_num_vec_elem:
return &on_number_stack;
case OP_string:
case OP_integer:
case OP_pos_int:
case OP_vector:
+ case OP_expr_node:
return ¬_on_stack;
default:
converted to type EXPECTED_TYPE, inserting a conversion at *N
if necessary. Returns true if successful, false on failure. */
static bool
-type_check (const struct expr_node *n, enum val_type expected_type)
+type_check (const struct expression *e, const struct expr_node *n,
+ enum val_type expected_type)
{
atom_type actual_type = expr_node_returns (n);
case VAL_NUMERIC:
if (actual_type != OP_number && actual_type != OP_boolean)
{
- msg (SE, _("Type mismatch: expression has %s type, "
- "but a numeric value is required here."),
+ msg_at (SE, expr_location (e, n),
+ _("Type mismatch: expression has type '%s', "
+ "but a numeric value is required."),
atom_type_name (actual_type));
return false;
}
case VAL_STRING:
if (actual_type != OP_string)
{
- msg (SE, _("Type mismatch: expression has %s type, "
- "but a string value is required here."),
+ msg_at (SE, expr_location (e, n),
+ _("Type mismatch: expression has type '%s', "
+ "but a string value is required."),
atom_type_name (actual_type));
return false;
}
/* Returns the source code location corresponding to expression NODE, computing
it lazily if needed. */
-static const struct msg_location *
-expr_location (struct expression *e, const struct expr_node *node_)
+const struct msg_location *
+expr_location (const struct expression *e_, const struct expr_node *node_)
{
struct expr_node *node = CONST_CAST (struct expr_node *, node_);
if (!node)
if (!node->location)
{
+ struct expression *e = CONST_CAST (struct expression *, e_);
const struct msg_location *min = NULL;
const struct msg_location *max = NULL;
expr_location__ (e, node, &min, &max);
*argp = expr_allocate_unary (e, OP_BOOLEAN_TO_NUM, arg);
return true;
}
+ else if (actual_type == OP_num_vec_elem)
+ {
+ if (do_coercion)
+ arg->type = OP_VEC_ELEM_NUM;
+ return true;
+ }
break;
case OP_string:
{
/* Convert numeric to boolean. */
if (do_coercion)
- *argp = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, arg);
+ *argp = expr_allocate_binary (e, OP_OPERAND_TO_BOOLEAN, arg,
+ expr_allocate_expr_node (e, node));
+ return true;
+ }
+ break;
+
+ case OP_integer:
+ if (actual_type == OP_number)
+ {
+ /* Convert number to integer. */
+ if (do_coercion)
+ *argp = expr_allocate_unary (e, OP_NUM_TO_INTEGER, arg);
return true;
}
break;
case OP_format:
+ /* We never coerce to OP_format, only to OP_ni_format or OP_no_format. */
NOT_REACHED ();
case OP_ni_format:
"not produce the mathematically expected result. "
"Use the AND logical operator to fix the problem "
"(e.g. `a < b AND b < c'). "
- "If chaining is really intended, parentheses will disable "
- "this warning (e.g. `(a < b) < c'.)");
+ "To disable this warning, insert parentheses.");
static const struct operator ops[] =
{
static const struct operator op = { .token = T_EXP, .num_op = OP_POW };
const char *chain_warning =
- _("The exponentiation operator (`**') is left-associative, "
- "even though right-associative semantics are more useful. "
- "That is, `a**b**c' equals `(a**b)**c', not as `a**(b**c)'. "
+ _("The exponentiation operator (`**') is left-associative: "
+ "`a**b**c' equals `(a**b)**c', not `a**(b**c)'. "
"To disable this warning, insert parentheses.");
if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
int start_ofs = lex_ofs (lexer);
struct expr_node *lhs = expr_allocate_number (e, -lex_tokval (lexer));
lex_get (lexer);
+ expr_add_location (lexer, e, start_ofs, lhs);
struct expr_node *node = parse_binary_operators__ (
lexer, e, &op, 1, parse_primary, chain_warning, lhs);
return node;
}
+static double
+ymd_to_offset (int y, int m, int d)
+{
+ char *error;
+ double retval = calendar_gregorian_to_offset (
+ y, m, d, settings_get_fmt_settings (), &error);
+ if (error)
+ {
+ msg (SE, "%s", error);
+ free (error);
+ }
+ return retval;
+}
+
+static struct expr_node *
+expr_date (struct expression *e, int year_digits)
+{
+ static const char *months[12] =
+ {
+ "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+ "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
+ };
+
+ time_t last_proc_time = time_of_last_procedure (e->ds);
+ struct tm *time = localtime (&last_proc_time);
+
+ char *tmp = (year_digits == 2
+ ? xasprintf ("%02d-%s-%02d", time->tm_mday, months[time->tm_mon],
+ time->tm_year % 100)
+ : xasprintf ("%02d-%s-%04d", time->tm_mday, months[time->tm_mon],
+ time->tm_year + 1900));
+
+ struct substring s;
+ ss_alloc_substring_pool (&s, ss_cstr (tmp), e->expr_pool);
+
+ free (tmp);
+
+ return expr_allocate_string (e, s);
+}
+
/* Parses system variables. */
static struct expr_node *
parse_sysvar (struct lexer *lexer, struct expression *e)
if (lex_match_id (lexer, "$CASENUM"))
return expr_allocate_nullary (e, OP_CASENUM);
else if (lex_match_id (lexer, "$DATE"))
- {
- static const char *months[12] =
- {
- "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
- "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
- };
-
- time_t last_proc_time = time_of_last_procedure (e->ds);
- struct tm *time;
- char temp_buf[10];
- struct substring s;
-
- time = localtime (&last_proc_time);
- sprintf (temp_buf, "%02d %s %02d", abs (time->tm_mday) % 100,
- months[abs (time->tm_mon) % 12], abs (time->tm_year) % 100);
-
- ss_alloc_substring (&s, ss_cstr (temp_buf));
- return expr_allocate_string (e, s);
- }
+ return expr_date (e, 2);
+ else if (lex_match_id (lexer, "$DATE11"))
+ return expr_date (e, 4);
else if (lex_match_id (lexer, "$TRUE"))
return expr_allocate_boolean (e, 1.0);
else if (lex_match_id (lexer, "$FALSE"))
{
time_t time = time_of_last_procedure (e->ds);
struct tm *tm = localtime (&time);
- return expr_allocate_number (e, expr_ymd_to_ofs (tm->tm_year + 1900,
- tm->tm_mon + 1,
- tm->tm_mday));
+ return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday));
}
else if (lex_match_id (lexer, "$TIME"))
{
time_t time = time_of_last_procedure (e->ds);
struct tm *tm = localtime (&time);
- return expr_allocate_number (e,
- expr_ymd_to_date (tm->tm_year + 1900,
+ return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday)
+ tm->tm_hour * 60 * 60.
case T_LPAREN:
{
- /* Count number of left parentheses so that we can match them against
- an equal number of right parentheses. This defeats trivial attempts
- to exhaust the stack with a lot of left parentheses. (More
- sophisticated attacks will still succeed.) */
- size_t n = 0;
- while (lex_match (lexer, T_LPAREN))
- n++;
-
+ lex_get (lexer);
struct expr_node *node = parse_or (lexer, e);
- if (!node)
- return NULL;
-
- for (size_t i = 0; i < n; i++)
- if (!lex_force_match (lexer, T_RPAREN))
- return NULL;
-
- return node;
+ return !node || !lex_force_match (lexer, T_RPAREN) ? NULL : node;
}
default:
return NULL;
operation_type type = (vector_get_type (vector) == VAL_NUMERIC
- ? OP_VEC_ELEM_NUM : OP_VEC_ELEM_STR);
+ ? OP_VEC_ELEM_NUM_RAW : OP_VEC_ELEM_STR);
struct expr_node *node = expr_allocate_binary (
e, type, element, expr_allocate_vector (e, vector));
expr_add_location (lexer, e, vector_start_ofs, node);
- if (!type_coercion (e, node, 1))
+ if (!type_coercion (e, node, 0))
{
msg_at (SE, expr_location (e, node),
_("A vector index must be numeric."));
}
static bool
-validate_function_args (const struct operation *f, int n_args, int min_valid)
+validate_function_args (const struct expression *e, const struct expr_node *n,
+ const struct operation *f, int n_args, int min_valid)
{
/* Count the function arguments that go into the trailing array (if any). We
know that there must be at least the minimum number because
here. */
assert (f->array_granularity == 2);
assert (n_args % 2 == 0);
- msg (SE, _("%s must have an odd number of arguments."), f->prototype);
+ msg_at (SE, expr_location (e, n),
+ _("%s must have an odd number of arguments."), f->prototype);
return false;
}
if (f->array_min_elems == 0)
{
assert ((f->flags & OPF_MIN_VALID) == 0);
- msg (SE, _("%s function cannot accept suffix .%d to specify the "
- "minimum number of valid arguments."),
- f->prototype, min_valid);
+ msg_at (SE, expr_location (e, n),
+ _("%s function cannot accept suffix .%d to specify the "
+ "minimum number of valid arguments."),
+ f->prototype, min_valid);
return false;
}
else
assert (f->flags & OPF_MIN_VALID);
if (min_valid > array_n_args)
{
- msg (SE, _("For %s with %d arguments, at most %d (not %d) may be "
- "required to be valid."),
- f->prototype, n_args, array_n_args, min_valid);
+ msg_at (SE, expr_location (e, n),
+ _("For %s with %d arguments, at most %d (not %d) may be "
+ "required to be valid."),
+ f->prototype, n_args, array_n_args, min_valid);
return false;
}
}
static void
no_match (struct expression *e, const char *func_name, struct expr_node *node,
- const struct operation *first, const struct operation *last)
+ const struct operation *ops, size_t n)
{
struct string s;
- const struct operation *f;
ds_init_empty (&s);
- if (last - first == 1)
+ if (n == 1)
{
- ds_put_format (&s, _("Type mismatch invoking %s as "), first->prototype);
+ ds_put_format (&s, _("Type mismatch invoking %s as "), ops->prototype);
put_invocation (&s, func_name, node);
}
else
put_invocation (&s, func_name, node);
ds_put_cstr (&s, _(" does not match any known function. Candidates are:"));
- for (f = first; f < last; f++)
- ds_put_format (&s, "\n%s", f->prototype);
+ for (size_t i = 0; i < n; i++)
+ ds_put_format (&s, "\n%s", ops[i].prototype);
}
ds_put_byte (&s, '.');
msg_at (SE, expr_location (e, node), "%s", ds_cstr (&s));
+ if (n == 1 && ops->n_args == node->n_args)
+ {
+ for (size_t i = 0; i < ops->n_args; i++)
+ if ((ops->args[i] == OP_ni_format
+ || ops->args[i] == OP_no_format)
+ && expr_node_returns (node->args[i]) == OP_format)
+ {
+ const struct fmt_spec *f = &node->args[i]->format;
+ char *error = fmt_check__ (f, (ops->args[i] == OP_ni_format
+ ? FMT_FOR_INPUT : FMT_FOR_OUTPUT));
+ if (!error)
+ error = fmt_check_type_compat__ (f, VAL_NUMERIC);
+ if (error)
+ {
+ msg_at (SN, expr_location (e, node->args[i]), "%s", error);
+ free (error);
+ }
+ }
+ }
+
ds_destroy (&s);
}
const struct operation *f = match_function (n, first, last);
if (!f)
{
- no_match (e, ds_cstr (&func_name), n, first, last);
+ no_match (e, ds_cstr (&func_name), n, first, last - first);
goto fail;
}
n->type = f - operations;
arguments were coercible. */
NOT_REACHED ();
}
- if (!validate_function_args (f, n_args, min_valid))
+ if (!validate_function_args (e, n, f, n_args, min_valid))
goto fail;
if ((f->flags & OPF_EXTENSION) && settings_get_syntax () == COMPATIBLE)
- msg (SW, _("%s is a PSPP extension."), f->prototype);
+ msg_at (SW, expr_location (e, n),
+ _("%s is a PSPP extension."), f->prototype);
if (f->flags & OPF_UNIMPLEMENTED)
{
- msg (SE, _("%s is not available in this version of PSPP."),
- f->prototype);
+ msg_at (SE, expr_location (e, n),
+ _("%s is not available in this version of PSPP."), f->prototype);
goto fail;
}
if ((f->flags & OPF_PERM_ONLY) &&
proc_in_temporary_transformations (e->ds))
{
- msg (SE, _("%s may not appear after %s."), f->prototype, "TEMPORARY");
+ msg_at (SE, expr_location (e, n),
+ _("%s may not appear after %s."), f->prototype, "TEMPORARY");
goto fail;
}
return n;
}
+struct expr_node *
+expr_allocate_expr_node (struct expression *e,
+ const struct expr_node *expr_node)
+{
+ struct expr_node *n = pool_alloc (e->expr_pool, sizeof *n);
+ *n = (struct expr_node) { .type = OP_expr_node, .expr_node = expr_node };
+ return n;
+}
+
/* Allocates a unary composite node that represents the value of
variable V in expression E. */
static struct expr_node *