{
struct expression *condition;
- condition = expr_parse (lexer, ds, EXPR_BOOLEAN);
+ condition = expr_parse_bool (lexer, NULL, ds);
if (condition == NULL)
return CMD_CASCADING_FAILURE;
return false;
}
- *condition = expr_parse_pool (lexer, loop->pool, loop->ds, EXPR_BOOLEAN);
+ *condition = expr_parse_bool (lexer, loop->pool, loop->ds);
return *condition != NULL;
}
if (!lex_force_match (lexer, T_EQUALS))
return false;
- loop->first_expr = expr_parse_pool (lexer, loop->pool,
- loop->ds, EXPR_NUMBER);
+ loop->first_expr = expr_parse (lexer, loop->pool, loop->ds, VAL_NUMERIC);
if (loop->first_expr == NULL)
return false;
lex_sbc_only_once (e == &loop->last_expr ? "TO" : "BY");
return false;
}
- *e = expr_parse_pool (lexer, loop->pool, loop->ds, EXPR_NUMBER);
+ *e = expr_parse (lexer, loop->pool, loop->ds, VAL_NUMERIC);
if (*e == NULL)
return false;
}
goto error;
}
- e = expr_parse (lexer, ds, EXPR_NUMBER);
+ e = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
if (!e)
goto error;
}
if (lex_token (lexer) != T_ENDCMD)
{
- expr = expr_parse (lexer, ds, EXPR_NUMBER);
+ expr = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
if (lex_token (lexer) != T_ENDCMD)
{
lex_error (lexer, _("expecting end of command"));
static const char *atom_type_name (atom_type);
static struct expression *finish_expression (union any_node *,
struct expression *);
-static bool type_check (struct expression *, union any_node **,
- enum expr_type expected_type);
+static bool type_check (const union any_node *, enum val_type expected_type);
static union any_node *allocate_unary_variable (struct expression *,
const struct variable *);
\f
/* Public functions. */
-/* Parses an expression of the given TYPE.
- If DICT 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 expression if successful or a null pointer
- otherwise. */
+/* 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
+ expression if successful or a null pointer otherwise. If POOL is nonnull,
+ then destroying POOL will free the expression; otherwise, the caller must
+ eventually free it with expr_free(). */
struct expression *
-expr_parse (struct lexer *lexer, struct dataset *ds, enum expr_type type)
+expr_parse (struct lexer *lexer, struct pool *pool, struct dataset *ds,
+ enum val_type type)
{
- union any_node *n;
- struct expression *e;
+ assert (val_type_is_valid (type));
- assert (type == EXPR_NUMBER || type == EXPR_STRING || type == EXPR_BOOLEAN);
+ struct expression *e = expr_create (ds);
+ union any_node *n = parse_or (lexer, e);
+ if (!n || !type_check (n, type))
+ {
+ expr_free (e);
+ return NULL;
+ }
- e = expr_create (ds);
- n = parse_or (lexer, e);
- if (n != NULL && type_check (e, &n, type))
- return finish_expression (expr_optimize (n, e), e);
- else
+ e = finish_expression (expr_optimize (n, e), e);
+ if (pool)
+ pool_add_subpool (pool, e->expr_pool);
+ return e;
+}
+
+/* Parses a boolean expression, otherwise similar to expr_parse(). */
+struct expression *
+expr_parse_bool (struct lexer *lexer, struct pool *pool, struct dataset *ds)
+{
+ struct expression *e = expr_create (ds);
+ union any_node *n = parse_or (lexer, e);
+ if (!n)
{
expr_free (e);
return NULL;
}
+
+ atom_type actual_type = expr_node_returns (n);
+ if (actual_type == OP_number)
+ n = expr_allocate_binary (e, OP_NUM_TO_BOOLEAN, n,
+ expr_allocate_string (e, ss_empty ()));
+ else if (actual_type != OP_boolean)
+ {
+ msg (SE, _("Type mismatch: expression has %s type, "
+ "but a boolean value is required here."),
+ atom_type_name (actual_type));
+ expr_free (e);
+ return NULL;
+ }
+
+ e = finish_expression (expr_optimize (n, e), e);
+ if (pool)
+ pool_add_subpool (pool, e->expr_pool);
+ return e;
}
-/* Parses and returns an expression of the given TYPE, as
- expr_parse(), and sets up so that destroying POOL will free
- the expression as well. */
+/* Parses a numeric expression that is intended to be assigned to newly created
+ variable NEW_VAR_NAME. (This allows for a better error message if the
+ expression is not numeric.) Otherwise similar to expr_parse(). */
struct expression *
-expr_parse_pool (struct lexer *lexer,
- struct pool *pool,
- struct dataset *ds,
- enum expr_type type)
+expr_parse_new_variable (struct lexer *lexer, struct pool *pool, struct dataset *ds,
+ const char *new_var_name)
{
- struct expression *e = expr_parse (lexer, ds, type);
- if (e != NULL)
+ struct expression *e = expr_create (ds);
+ union any_node *n = parse_or (lexer, e);
+ if (!n)
+ {
+ expr_free (e);
+ return NULL;
+ }
+
+ atom_type actual_type = expr_node_returns (n);
+ if (actual_type != OP_number && actual_type != OP_boolean)
+ {
+ msg (SE, _("This command tries to create a new variable %s by assigning a "
+ "string value to it, but this is not supported. Use "
+ "the STRING command to create the new variable with the "
+ "correct width before assigning to it, e.g. STRING %s(A20)."),
+ new_var_name, new_var_name);
+ expr_free (e);
+ return NULL;
+ }
+
+ e = finish_expression (expr_optimize (n, e), e);
+ if (pool)
pool_add_subpool (pool, e->expr_pool);
return e;
}
converted to type EXPECTED_TYPE, inserting a conversion at *N
if necessary. Returns true if successful, false on failure. */
static bool
-type_check (struct expression *e,
- union any_node **n, enum expr_type expected_type)
+type_check (const union any_node *n, enum val_type expected_type)
{
- atom_type actual_type = expr_node_returns (*n);
+ atom_type actual_type = expr_node_returns (n);
switch (expected_type)
{
- case EXPR_BOOLEAN:
- case EXPR_NUMBER:
+ case VAL_NUMERIC:
if (actual_type != OP_number && actual_type != OP_boolean)
{
msg (SE, _("Type mismatch: expression has %s type, "
atom_type_name (actual_type));
return false;
}
- if (actual_type == OP_number && expected_type == EXPR_BOOLEAN)
- *n = expr_allocate_binary (e, OP_NUM_TO_BOOLEAN, *n,
- expr_allocate_string (e, ss_empty ()));
break;
- case EXPR_STRING:
+ case VAL_STRING:
if (actual_type != OP_string)
{
msg (SE, _("Type mismatch: expression has %s type, "
#include <stddef.h>
-/* Expression parsing flags. */
-enum expr_type
- {
- EXPR_NUMBER = 0xf000, /* Number. */
- EXPR_STRING, /* String. */
- EXPR_BOOLEAN, /* Boolean (number limited to 0, 1, SYSMIS). */
- };
+#include "data/val-type.h"
+struct ccase;
+struct dataset;
struct dictionary;
struct expression;
-struct ccase;
+struct lexer;
struct pool;
union value;
-struct dataset ;
-struct lexer ;
-
-struct expression *expr_parse (struct lexer *lexer, struct dataset *, enum expr_type);
-struct expression *expr_parse_pool (struct lexer *,
- struct pool *,
- struct dataset *,
- enum expr_type);
+
+struct expression *expr_parse (struct lexer *lexer, struct pool *,
+ struct dataset *, enum val_type);
+struct expression *expr_parse_bool (struct lexer *lexer, struct pool *,
+ struct dataset *);
+struct expression *expr_parse_new_variable (struct lexer *lexer, struct pool *,
+ struct dataset *,
+ const char *new_var_name);
void expr_free (struct expression *);
struct dataset;
struct compute_trns;
struct lvalue;
+/* COMPUTE or IF target variable or vector element.
+ For a variable, the `variable' member is non-null.
+ For a vector element, the `vector' member is non-null. */
+struct lvalue
+ {
+ struct variable *variable; /* Destination variable. */
+ bool is_new_variable; /* Did we create the variable? */
+
+ const struct vector *vector; /* Destination vector, if any, or NULL. */
+ struct expression *element; /* Destination vector element, or NULL. */
+ };
+
/* Target of a COMPUTE or IF assignment, either a variable or a
vector element. */
static struct lvalue *lvalue_parse (struct lexer *lexer, struct dataset *);
compute = compute_trns_create ();
/* Test expression. */
- compute->test = expr_parse (lexer, ds, EXPR_BOOLEAN);
+ compute->test = expr_parse_bool (lexer, NULL, ds);
if (compute->test == NULL)
goto fail;
parse_rvalue (struct lexer *lexer,
const struct lvalue *lvalue, struct dataset *ds)
{
- bool is_numeric = lvalue_get_type (lvalue) == VAL_NUMERIC;
-
- return expr_parse (lexer, ds, is_numeric ? EXPR_NUMBER : EXPR_STRING);
+ if (lvalue->is_new_variable)
+ return expr_parse_new_variable (lexer, NULL, ds, var_get_name (lvalue->variable));
+ else
+ return expr_parse (lexer, NULL, ds, lvalue_get_type (lvalue));
}
/* Returns a new struct compute_trns after initializing its fields. */
return true;
}
\f
-/* COMPUTE or IF target variable or vector element.
- For a variable, the `variable' member is non-null.
- For a vector element, the `vector' member is non-null. */
-struct lvalue
- {
- struct variable *variable; /* Destination variable. */
- bool is_new_variable; /* Did we create the variable? */
-
- const struct vector *vector; /* Destination vector, if any, or NULL. */
- struct expression *element; /* Destination vector element, or NULL. */
- };
-
/* Parses the target variable or vector element into a new
`struct lvalue', which is returned. */
static struct lvalue *
lex_get (lexer);
if (!lex_force_match (lexer, T_LPAREN))
goto lossage;
- lvalue->element = expr_parse (lexer, ds, EXPR_NUMBER);
+ lvalue->element = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
if (lvalue->element == NULL)
goto lossage;
if (!lex_force_match (lexer, T_RPAREN))
struct expression *e;
struct select_if_trns *t;
- e = expr_parse (lexer, ds, EXPR_BOOLEAN);
+ e = expr_parse_bool (lexer, NULL, ds);
if (!e)
return CMD_CASCADING_FAILURE;
])
AT_CLEANUP
-AT_SETUP([parsing numeric expression with type mismatch])
+AT_SETUP([parsing boolean expression with type mismatch])
AT_DATA([parse.sps], [dnl
DATA LIST NOTABLE/x 1(A).
IF 'foo'.
])
AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:2: error: IF: Type mismatch: expression has string type, but a numeric value is required here."
+"parse.sps:2: error: IF: Type mismatch: expression has string type, but a boolean value is required here."
+])
+AT_CLEANUP
+
+AT_SETUP([parsing numeric expression with type mismatch])
+AT_DATA([parse.sps], [dnl
+DATA LIST NOTABLE/x 1.
+COMPUTE x='foo'.
+])
+AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
+"parse.sps:2: error: COMPUTE: Type mismatch: expression has string type, but a numeric value is required here."
])
AT_CLEANUP
])
AT_CLEANUP
+AT_SETUP([assigning string expression to new variable])
+AT_KEYWORDS([expression negative])
+AT_DATA([parse.sps], [dnl
+DATA LIST NOTABLE/x 1(A).
+COMPUTE y='a'.
+])
+AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
+"parse.sps:2: error: COMPUTE: This command tries to create a new variable y by assigning a string value to it, but this is not supported. Use the STRING command to create the new variable with the correct width before assigning to it, e.g. STRING y(A20)."
+])
+AT_CLEANUP
+
AT_SETUP([parse expression with unknown system variable])
AT_KEYWORDS([expression negative])
AT_DATA([parse.sps], [dnl