int main(){ struct Variable var; var_init(&var, "lol", "lolllvm", V_INT); assert (strcmp("lol", var_get_name(&var)) == 0); assert (strcmp("lolllvm", var_get_llvm_name(&var)) == 0); assert (var_is_int(&var) != 0); assert (var_is_float(&var) == 0); assert (var_get_type(&var) == V_INT); assert (var_is_special(&var) == 0); var_init(&var, "lol", "lolllvm", V_FLOAT|V_SPECIAL); var_info(&var); assert (var_is_int(&var) == 0); assert (var_is_float(&var) != 0); assert (var_get_type(&var) == V_FLOAT); assert (var_is_special(&var) != 0); var_set_value(&var, -131); assert (var_get_value(&var) == -131); var_set_value(&var, 1.234); assert (((var_get_value(&var)) - 1.234) < 0.00001); assert (var_get_value(&var) != 1); var_set_modified(&var, V_MODIFIED); assert (var_is_modified(&var) != 0); var_set_modified(&var, V_NULL); assert (var_is_modified(&var) == 0); var_set_type(&var, V_FLOAT); assert (var_is_int(&var) == 0); assert (var_is_float(&var) != 0); assert (var_get_type(&var) == V_FLOAT); var_set_special(&var, V_SPECIAL); assert (var_is_special(&var) != 0); var_set_type(&var, V_INT); assert (var_is_int(&var) != 0); assert (var_is_float(&var) == 0); assert (var_get_type(&var) == V_INT); var_set_access(&var, V_NULL); assert (var_is_writable(&var) == 0); var_set_access(&var, V_WRITABLE); assert (var_is_writable(&var) != 0); var.flags |= 1; assert (var_is_declared(&var) != 0); /* var_set_nb_args(&var, 13); */ /* assert (var_get_nb_args(&var) == 13); */ printf("Tests OK (sauf get_nb_args)\n"); }
static int operate(struct _var *var_d, struct _var *var_s, struct _operator *operator) { #ifdef DEBUG printf(">>> OPERATING ON %d/%f/%d (%d) %d/%f/%d\n", var_get_int32(var_d), var_get_float(var_d), var_get_type(var_d), operator->operation, var_get_int32(var_s), var_get_float(var_s), var_get_type(var_s)); #endif switch(operator->operation) { case OPER_NOT: return var_not(var_d); case OPER_MUL: return var_mul(var_d, var_s); case OPER_DIV: return var_div(var_d, var_s); case OPER_MOD: return var_mod(var_d, var_s); case OPER_PLUS: return var_add(var_d, var_s); case OPER_MINUS: return var_sub(var_d, var_s); case OPER_LEFT_SHIFT: return var_shift_left(var_d, var_s); case OPER_RIGHT_SHIFT: return var_shift_right(var_d, var_s); case OPER_AND: return var_and(var_d, var_s); case OPER_XOR: return var_xor(var_d, var_s); case OPER_OR: return var_or(var_d, var_s); default: printf("Internal Error: WTF, bad operator %d\n", operator->operation); return 0; } }
int cmd_missing_values (struct lexer *lexer, struct dataset *ds) { struct dictionary *dict = dataset_dict (ds); struct variable **v = NULL; size_t nv; bool ok = true; while (lex_token (lexer) != T_ENDCMD) { size_t i; if (!parse_variables (lexer, dict, &v, &nv, PV_NONE)) goto error; if (!lex_force_match (lexer, T_LPAREN)) goto error; for (i = 0; i < nv; i++) var_clear_missing_values (v[i]); if (!lex_match (lexer, T_RPAREN)) { struct missing_values mv; for (i = 0; i < nv; i++) if (var_get_type (v[i]) != var_get_type (v[0])) { const struct variable *n = var_is_numeric (v[0]) ? v[0] : v[i]; const struct variable *s = var_is_numeric (v[0]) ? v[i] : v[0]; msg (SE, _("Cannot mix numeric variables (e.g. %s) and " "string variables (e.g. %s) within a single list."), var_get_name (n), var_get_name (s)); goto error; } if (var_is_numeric (v[0])) { mv_init (&mv, 0); while (!lex_match (lexer, T_RPAREN)) { enum fmt_type type = var_get_print_format (v[0])->type; double x, y; bool ok; if (!parse_num_range (lexer, &x, &y, &type)) goto error; ok = (x == y ? mv_add_num (&mv, x) : mv_add_range (&mv, x, y)); if (!ok) ok = false; lex_match (lexer, T_COMMA); } } else { mv_init (&mv, MV_MAX_STRING); while (!lex_match (lexer, T_RPAREN)) { uint8_t value[MV_MAX_STRING]; char *dict_mv; size_t length; if (!lex_force_string (lexer)) { ok = false; break; } dict_mv = recode_string (dict_get_encoding (dict), "UTF-8", lex_tokcstr (lexer), ss_length (lex_tokss (lexer))); length = strlen (dict_mv); if (length > MV_MAX_STRING) { /* XXX truncate graphemes not bytes */ msg (SE, _("Truncating missing value to maximum " "acceptable length (%d bytes)."), MV_MAX_STRING); length = MV_MAX_STRING; } memset (value, ' ', MV_MAX_STRING); memcpy (value, dict_mv, length); free (dict_mv); if (!mv_add_str (&mv, value)) ok = false; lex_get (lexer); lex_match (lexer, T_COMMA); } } for (i = 0; i < nv; i++) { if (mv_is_resizable (&mv, var_get_width (v[i]))) var_set_missing_values (v[i], &mv); else { msg (SE, _("Missing values provided are too long to assign " "to variable of width %d."), var_get_width (v[i])); ok = false; } } mv_destroy (&mv); } lex_match (lexer, T_SLASH); free (v); v = NULL; } free (v); return ok ? CMD_SUCCESS : CMD_FAILURE; error: free (v); return CMD_FAILURE; }
/* Returns FALSE if the variables represented by the union of the rows currently selected by SOURCE widget, and contents of the DEST widget, are of different types. In other words, this function when passed as the argument to psppire_selector_set_allow, ensures that the selector selects only string variables, or only numeric variables, not a mixture. */ gboolean homogeneous_types (GtkWidget *source, GtkWidget *dest) { gboolean ok; GtkTreeIter iter; gboolean retval = TRUE; GtkTreeModel *top_model = gtk_tree_view_get_model (GTK_TREE_VIEW (source)); GtkTreeModel *model; PsppireDict *dict; GtkTreeSelection *selection; enum val_type type; GList *list, *l; bool have_type; get_base_model (top_model, NULL, &model, NULL); dict = PSPPIRE_DICT (model); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source)); list = gtk_tree_selection_get_selected_rows (selection, &model); /* Iterate through the selection of the source treeview */ have_type = false; for (l = list; l ; l = l->next) { GtkTreePath *p = get_base_tree_path (top_model, l->data); gint *idx = gtk_tree_path_get_indices (p); const struct variable *v = psppire_dict_get_variable (dict, idx[0]); gtk_tree_path_free (p); if (have_type && var_get_type (v) != type) { retval = FALSE; break; } type = var_get_type (v); have_type = true; } g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); g_list_free (list); if ( retval == FALSE ) return FALSE; /* now deal with the dest widget */ model = gtk_tree_view_get_model (GTK_TREE_VIEW (dest)); for (ok = gtk_tree_model_get_iter_first (model, &iter); ok; ok = gtk_tree_model_iter_next (model, &iter)) { const struct variable *v; gtk_tree_model_get (model, &iter, 0, &v, -1); if ( have_type && var_get_type (v) != type ) { retval = FALSE; break; } type = var_get_type (v); have_type = true; } return retval; }
static int eval_expression_go(struct _asm_context *asm_context, struct _var *var, struct _operator *last_operator) { char token[TOKENLEN]; int token_type; struct _var var_stack[3]; int var_stack_ptr = 1; struct _operator operator; int last_token_was_op = -1; #ifdef DEBUG printf("Enter eval_expression_go, var=%d/%f/%d\n", var_get_int32(var), var_get_float(var), var_get_type(var)); #endif memcpy(&operator, last_operator, sizeof(struct _operator)); VAR_COPY(&var_stack[0], var); while(1) { #ifdef DEBUG printf("eval_expression> going to grab a token\n"); #endif token_type = tokens_get(asm_context, token, TOKENLEN); #ifdef DEBUG printf("eval_expression> token=%s var_stack_ptr=%d\n", token, var_stack_ptr); #endif // Issue 15: Return an error if a stack is full with noe operator. if (var_stack_ptr == 3 && operator.operation == OPER_UNSET) { return -1; } if (token_type == TOKEN_QUOTED) { if (token[0] == '\\') { int e = tokens_escape_char(asm_context, (unsigned char *)token); if (e == 0) return -1; if (token[e+1] != 0) { print_error("Quoted literal too long.", asm_context); return -1; } sprintf(token, "%d", token[e]); } else { if (token[1]!=0) { print_error("Quoted literal too long.", asm_context); return -1; } sprintf(token, "%d", token[0]); } token_type = TOKEN_NUMBER; } // Open and close parenthesis if (IS_TOKEN(token,'(')) { if (last_token_was_op == 0 && operator.operation != OPER_UNSET) { operate(&var_stack[var_stack_ptr-2], &var_stack[var_stack_ptr-1], last_operator); var_stack_ptr--; operator.operation = OPER_UNSET; VAR_COPY(var, &var_stack[var_stack_ptr-1]); tokens_push(asm_context, token, token_type); return 0; } if (operator.operation == OPER_UNSET && var_stack_ptr == 2) { // This is probably the x(r12) case.. so this is actually okay VAR_COPY(var, &var_stack[var_stack_ptr-1]); tokens_push(asm_context, token, token_type); return 0; } struct _var paren_var; struct _operator paren_operator; paren_operator.precedence = PREC_UNSET; paren_operator.operation = OPER_UNSET; memset(&paren_var, 0, sizeof(struct _var)); if (eval_expression_go(asm_context, &paren_var, &paren_operator) != 0) { return -1; } last_token_was_op = 0; #ifdef DEBUG printf("Paren got back %d/%f/%d\n", var_get_int32(&paren_var), var_get_float(&paren_var), var_get_type(&paren_var)); #endif VAR_COPY(&var_stack[var_stack_ptr++], &paren_var); token_type = tokens_get(asm_context, token, TOKENLEN); if (!(token[1] == 0 && token[0] == ')')) { print_error("No matching ')'", asm_context); return -1; } continue; } if (IS_TOKEN(token,')')) { tokens_push(asm_context, token, token_type); break; } // End of expression if (IS_TOKEN(token,',') || IS_TOKEN(token,']') || token_type == TOKEN_EOF || IS_TOKEN(token,'.')) { tokens_push(asm_context, token, token_type); break; } if (token_type == TOKEN_EOL) { //asm_context->tokens.line++; tokens_push(asm_context, token, token_type); break; } // Read number if (token_type == TOKEN_NUMBER) { last_token_was_op = 0; if (var_stack_ptr == 3) { print_error_unexp(token, asm_context); return -1; } var_set_int(&var_stack[var_stack_ptr++], atoll(token)); } else if (token_type == TOKEN_FLOAT) { if (var_stack_ptr == 3) { print_error_unexp(token, asm_context); return -1; } var_set_float(&var_stack[var_stack_ptr++], atof(token)); } else if (token_type == TOKEN_SYMBOL) { last_token_was_op = 1; struct _operator operator_prev; memcpy(&operator_prev, &operator, sizeof(struct _operator)); if (get_operator(token, &operator) == -1) { print_error_unexp(token, asm_context); return -1; } // Issue 15: 2015-July-21 mkohn - If operator is ~ then reverse // the next number. if (operator.operation == OPER_NOT) { int64_t num; if (parse_unary(asm_context, &num, OPER_NOT) != 0) { return -1; } if (var_stack_ptr == 3) { print_error_unexp(token, asm_context); return -1; } var_set_int(&var_stack[var_stack_ptr++], num); memcpy(&operator, &operator_prev, sizeof(struct _operator)); last_token_was_op = 0; continue; } // Stack pointer probably shouldn't be less than 2 if (var_stack_ptr == 0) { printf("Error: Unexpected operator '%s' at %s:%d\n", token, asm_context->tokens.filename, asm_context->tokens.line); return -1; } #ifdef DEBUG printf("TOKEN %s: precedence %d %d\n", token, last_operator->precedence, operator.precedence); #endif if (last_operator->precedence == PREC_UNSET) { memcpy(last_operator, &operator, sizeof(struct _operator)); } else if (last_operator->precedence > operator.precedence) { if (eval_expression_go(asm_context, &var_stack[var_stack_ptr-1], &operator) == -1) { return -1; } } else { operate(&var_stack[var_stack_ptr-2], &var_stack[var_stack_ptr-1], last_operator); var_stack_ptr--; memcpy(last_operator, &operator, sizeof(struct _operator)); } } else { if (asm_context->pass != 1) { print_error_unexp(token, asm_context); } return -1; } } #ifdef DEBUG printf("going to leave operation=%d\n", last_operator->operation); PRINT_STACK() #endif if (last_operator->operation != OPER_UNSET) { operate(&var_stack[var_stack_ptr-2], &var_stack[var_stack_ptr-1], last_operator); var_stack_ptr--; } VAR_COPY(var, &var_stack[var_stack_ptr-1]); return 0; }
/* Returns FALSE if the variables represented by the union of the rows currently selected by SOURCE widget, and contents of the DEST widget, are of different types. In other words, this function when passed as the argument to psppire_selector_set_allow, ensures that the selector selects only string variables, or only numeric variables, not a mixture. */ gboolean homogeneous_types (GtkWidget *source, GtkWidget *dest) { gboolean ok; GtkTreeIter iter; gboolean retval = TRUE; GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (source)); PsppireDict *dict; GtkTreeSelection *selection; enum val_type type = -1; GList *list, *l; while (GTK_IS_TREE_MODEL_FILTER (model)) { model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model)); } dict = PSPPIRE_DICT (model); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source)); list = gtk_tree_selection_get_selected_rows (selection, &model); /* Iterate through the selection of the source treeview */ for (l = list; l ; l = l->next) { GtkTreePath *path = l->data; GtkTreePath *fpath = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path); gint *idx = gtk_tree_path_get_indices (fpath); const struct variable *v = psppire_dict_get_variable (dict, idx[0]); gtk_tree_path_free (fpath); if ( type != -1 ) { if ( var_get_type (v) != type ) { retval = FALSE; break; } } type = var_get_type (v); } g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL); g_list_free (list); if ( retval == FALSE ) return FALSE; /* now deal with the dest widget */ model = gtk_tree_view_get_model (GTK_TREE_VIEW (dest)); for (ok = gtk_tree_model_get_iter_first (model, &iter); ok; ok = gtk_tree_model_iter_next (model, &iter)) { const struct variable *v; gtk_tree_model_get (model, &iter, 0, &v, -1); if ( type != -1 ) { if ( var_get_type (v) != type ) { retval = FALSE; break; } } type = var_get_type (v); } return retval; }
/* Parse all the aggregate functions. */ static bool parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict, struct agr_proc *agr) { struct agr_var *tail; /* Tail of linked list starting at agr->vars. */ /* Parse everything. */ tail = NULL; for (;;) { char **dest; char **dest_label; size_t n_dest; struct string function_name; enum mv_class exclude; const struct agr_func *function; int func_index; union agr_argument arg[2]; const struct variable **src; size_t n_src; size_t i; dest = NULL; dest_label = NULL; n_dest = 0; src = NULL; function = NULL; n_src = 0; arg[0].c = NULL; arg[1].c = NULL; ds_init_empty (&function_name); /* Parse the list of target variables. */ while (!lex_match (lexer, T_EQUALS)) { size_t n_dest_prev = n_dest; if (!parse_DATA_LIST_vars (lexer, dict, &dest, &n_dest, (PV_APPEND | PV_SINGLE | PV_NO_SCRATCH | PV_NO_DUPLICATE))) goto error; /* Assign empty labels. */ { int j; dest_label = xnrealloc (dest_label, n_dest, sizeof *dest_label); for (j = n_dest_prev; j < n_dest; j++) dest_label[j] = NULL; } if (lex_is_string (lexer)) { dest_label[n_dest - 1] = xstrdup (lex_tokcstr (lexer)); lex_get (lexer); } } /* Get the name of the aggregation function. */ if (lex_token (lexer) != T_ID) { lex_error (lexer, _("expecting aggregation function")); goto error; } ds_assign_substring (&function_name, lex_tokss (lexer)); exclude = ds_chomp_byte (&function_name, '.') ? MV_SYSTEM : MV_ANY; for (function = agr_func_tab; function->name; function++) if (!c_strcasecmp (function->name, ds_cstr (&function_name))) break; if (NULL == function->name) { msg (SE, _("Unknown aggregation function %s."), ds_cstr (&function_name)); goto error; } ds_destroy (&function_name); func_index = function - agr_func_tab; lex_get (lexer); /* Check for leading lparen. */ if (!lex_match (lexer, T_LPAREN)) { if (function->src_vars == AGR_SV_YES) { lex_force_match (lexer, T_LPAREN); goto error; } } else { /* Parse list of source variables. */ { int pv_opts = PV_NO_SCRATCH; if (func_index == SUM || func_index == MEAN || func_index == SD) pv_opts |= PV_NUMERIC; else if (function->n_args) pv_opts |= PV_SAME_TYPE; if (!parse_variables_const (lexer, dict, &src, &n_src, pv_opts)) goto error; } /* Parse function arguments, for those functions that require arguments. */ if (function->n_args != 0) for (i = 0; i < function->n_args; i++) { int type; lex_match (lexer, T_COMMA); if (lex_is_string (lexer)) { arg[i].c = recode_string (dict_get_encoding (agr->dict), "UTF-8", lex_tokcstr (lexer), -1); type = VAL_STRING; } else if (lex_is_number (lexer)) { arg[i].f = lex_tokval (lexer); type = VAL_NUMERIC; } else { msg (SE, _("Missing argument %zu to %s."), i + 1, function->name); goto error; } lex_get (lexer); if (type != var_get_type (src[0])) { msg (SE, _("Arguments to %s must be of same type as " "source variables."), function->name); goto error; } } /* Trailing rparen. */ if (!lex_force_match (lexer, T_RPAREN)) goto error; /* Now check that the number of source variables match the number of target variables. If we check earlier than this, the user can get very misleading error message, i.e. `AGGREGATE x=SUM(y t).' will get this error message when a proper message would be more like `unknown variable t'. */ if (n_src != n_dest) { msg (SE, _("Number of source variables (%zu) does not match " "number of target variables (%zu)."), n_src, n_dest); goto error; } if ((func_index == PIN || func_index == POUT || func_index == FIN || func_index == FOUT) && (var_is_numeric (src[0]) ? arg[0].f > arg[1].f : str_compare_rpad (arg[0].c, arg[1].c) > 0)) { union agr_argument t = arg[0]; arg[0] = arg[1]; arg[1] = t; msg (SW, _("The value arguments passed to the %s function " "are out-of-order. They will be treated as if " "they had been specified in the correct order."), function->name); } } /* Finally add these to the linked list of aggregation variables. */ for (i = 0; i < n_dest; i++) { struct agr_var *v = xzalloc (sizeof *v); /* Add variable to chain. */ if (agr->agr_vars != NULL) tail->next = v; else agr->agr_vars = v; tail = v; tail->next = NULL; v->moments = NULL; /* Create the target variable in the aggregate dictionary. */ { struct variable *destvar; v->function = func_index; if (src) { v->src = src[i]; if (var_is_alpha (src[i])) { v->function |= FSTRING; v->string = xmalloc (var_get_width (src[i])); } if (function->alpha_type == VAL_STRING) destvar = dict_clone_var_as (agr->dict, v->src, dest[i]); else { assert (var_is_numeric (v->src) || function->alpha_type == VAL_NUMERIC); destvar = dict_create_var (agr->dict, dest[i], 0); if (destvar != NULL) { struct fmt_spec f; if ((func_index == N || func_index == NMISS) && dict_get_weight (dict) != NULL) f = fmt_for_output (FMT_F, 8, 2); else f = function->format; var_set_both_formats (destvar, &f); } } } else { struct fmt_spec f; v->src = NULL; destvar = dict_create_var (agr->dict, dest[i], 0); if (destvar != NULL) { if ((func_index == N || func_index == NMISS) && dict_get_weight (dict) != NULL) f = fmt_for_output (FMT_F, 8, 2); else f = function->format; var_set_both_formats (destvar, &f); } } if (!destvar) { msg (SE, _("Variable name %s is not unique within the " "aggregate file dictionary, which contains " "the aggregate variables and the break " "variables."), dest[i]); goto error; } free (dest[i]); if (dest_label[i]) var_set_label (destvar, dest_label[i]); v->dest = destvar; } v->exclude = exclude; if (v->src != NULL) { int j; if (var_is_numeric (v->src)) for (j = 0; j < function->n_args; j++) v->arg[j].f = arg[j].f; else for (j = 0; j < function->n_args; j++) v->arg[j].c = xstrdup (arg[j].c); } } if (src != NULL && var_is_alpha (src[0])) for (i = 0; i < function->n_args; i++) { free (arg[i].c); arg[i].c = NULL; } free (src); free (dest); free (dest_label); if (!lex_match (lexer, T_SLASH)) { if (lex_token (lexer) == T_ENDCMD) return true; lex_error (lexer, "expecting end of command"); return false; } continue; error: ds_destroy (&function_name); for (i = 0; i < n_dest; i++) { free (dest[i]); free (dest_label[i]); } free (dest); free (dest_label); free (arg[0].c); free (arg[1].c); if (src && n_src && var_is_alpha (src[0])) for (i = 0; i < function->n_args; i++) { free (arg[i].c); arg[i].c = NULL; } free (src); return false; } }