Пример #1
0
static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
#define e1 (*ep1)
#define e2 (*ep2)
	struct expr *tmp, *tmp1, *tmp2;

	if (e1->type == type) {
		expr_eliminate_dups2(type, &e1->left.expr, &e2);
		expr_eliminate_dups2(type, &e1->right.expr, &e2);
		return;
	}
	if (e2->type == type) {
		expr_eliminate_dups2(type, &e1, &e2->left.expr);
		expr_eliminate_dups2(type, &e1, &e2->right.expr);
	}
	if (e1 == e2)
		return;

	switch (e1->type) {
	case E_OR:
		expr_eliminate_dups2(e1->type, &e1, &e1);
		// (FOO || BAR) && (!FOO && !BAR) -> n
		tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
		tmp2 = expr_copy(e2);
		tmp = expr_extract_eq_and(&tmp1, &tmp2);
		if (expr_is_yes(tmp1)) {
			expr_free(e1);
			e1 = expr_alloc_symbol(&symbol_no);
			trans_count++;
		}
		expr_free(tmp2);
		expr_free(tmp1);
		expr_free(tmp);
		break;
	case E_AND:
		expr_eliminate_dups2(e1->type, &e1, &e1);
		// (FOO && BAR) || (!FOO || !BAR) -> y
		tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
		tmp2 = expr_copy(e2);
		tmp = expr_extract_eq_or(&tmp1, &tmp2);
		if (expr_is_no(tmp1)) {
			expr_free(e1);
			e1 = expr_alloc_symbol(&symbol_yes);
			trans_count++;
		}
		expr_free(tmp2);
		expr_free(tmp1);
		expr_free(tmp);
		break;
	default:
		;
	}
#undef e1
#undef e2
}
Пример #2
0
struct expr *expr_copy(struct expr *e)
{
	if (!e) {
		return NULL;
	}
	struct expr *copy = expr_make(e->kind, NULL, NULL, NULL, e->constant);
	if (e->name) {
		copy->name = malloc(strlen(e->name) + 1);
		strcpy(copy->name, e->name);
	}
	copy->left = expr_copy(e->left);
	copy->right = expr_copy(e->right);
	copy->symbol = e->symbol;
	return copy;
}
Пример #3
0
struct stmt *stmt_copy(struct stmt const *stmt) {
    struct stmt *copy = malloc(sizeof(*copy));
    
    switch (copy->type = stmt->type) {
    case STMT_ASSIGN:
        copy->assign.identifier = strdup(stmt->assign.identifier);
        copy->assign.value = expr_copy(stmt->assign.value);
        break;
    case STMT_PRINT:
        copy->print = expr_copy(stmt->print);
        break;
    }

    return copy;
}
Пример #4
0
static int expr_eq(struct expr *e1, struct expr *e2)
{
	int res, old_count;

	if (e1->type != e2->type)
		return 0;
	switch (e1->type) {
	case E_EQUAL:
	case E_GEQ:
	case E_GTH:
	case E_LEQ:
	case E_LTH:
	case E_UNEQUAL:
		return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
	case E_SYMBOL:
		return e1->left.sym == e2->left.sym;
	case E_NOT:
		return expr_eq(e1->left.expr, e2->left.expr);
	case E_AND:
	case E_OR:
		e1 = expr_copy(e1);
		e2 = expr_copy(e2);
		old_count = trans_count;
		expr_eliminate_eq(&e1, &e2);
		res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
		       e1->left.sym == e2->left.sym);
		expr_free(e1);
		expr_free(e2);
		trans_count = old_count;
		return res;
	case E_LIST:
	case E_RANGE:
	case E_NONE:
		/* panic */;
	}

	if (DEBUG_EXPR) {
		expr_fprint(e1, stdout);
		printf(" = ");
		expr_fprint(e2, stdout);
		printf(" ?\n");
	}

	return 0;
}
Пример #5
0
void expr_list_append(struct expr_list **list, struct expr const *expr) {
    while (*list) {
        list = &(*list)->next;
    }

    *list = malloc(sizeof(**list));
    (*list)->expr = expr_copy(expr);
    (*list)->next = NULL;
}
Пример #6
0
static struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
{
	struct property *prop = prop_alloc(type, current_entry->sym);

	prop->menu = current_entry;
	prop->expr = expr;
	prop->visible.expr = menu_check_dep(dep);

	if (prompt) {
		/* For crostool-NG, a leading pipe followed with spaces
		 * means that pipe shall be removed, and the spaces should
		 * not be trimmed.
		 */
		if (*prompt == '|')
			prompt++;
		else if (isspace(*prompt)) {
			prop_warn(prop, "leading whitespace ignored");
			while (isspace(*prompt))
				prompt++;
		}
		if (current_entry->prompt && current_entry != &rootmenu)
			prop_warn(prop, "prompt redefined");

		/* Apply all upper menus' visibilities to actual prompts. */
		if(type == P_PROMPT) {
			struct menu *menu = current_entry;

			while ((menu = menu->parent) != NULL) {
				struct expr *dup_expr;

				if (!menu->visibility)
					continue;
				/*
				 * Do not add a reference to the
				 * menu's visibility expression but
				 * use a copy of it.  Otherwise the
				 * expression reduction functions
				 * will modify expressions that have
				 * multiple references which can
				 * cause unwanted side effects.
				 */
				dup_expr = expr_copy(menu->visibility);

				prop->visible.expr
					= expr_alloc_and(prop->visible.expr,
							 dup_expr);
			}
		}

		current_entry->prompt = prop;
	}
	prop->text = prompt;

	return prop;
}
Пример #7
0
static inline struct expr *
expr_get_leftmost_symbol(const struct expr *e)
{

	if (e == NULL)
		return NULL;

	while (e->type != E_SYMBOL)
		e = e->left.expr;

	return expr_copy(e);
}
Пример #8
0
expression *expr_copy(expression *orig)
{
  expression *copy;
  expression *oc;
  expression **cptr;

  if (NULL == orig)
    return NULL;

  /* fields */
  copy = (expression*)calloc(1,sizeof(expression));
  copy->type = orig->type;
  copy->qn = copy_qname(orig->qn);
  copy->xmlnode = orig->xmlnode;
  copy->ident = orig->ident ? strdup(orig->ident) : NULL;
  copy->axis = orig->axis;
  copy->num = orig->num;
  copy->str = orig->str ? strdup(orig->str) : NULL;
  copy->orig = orig->orig ? strdup(orig->orig) : NULL;
  copy->kind = orig->kind;

  /* branches */
  copy->r.test = expr_copy(orig->r.test);
  copy->r.left = expr_copy(orig->r.left);
  copy->r.right = expr_copy(orig->r.right);

  copy->r.name_avt = expr_copy(orig->r.name_avt);
  copy->r.value_avt = expr_copy(orig->r.value_avt);
  copy->r.namespace_avt = expr_copy(orig->r.namespace_avt);

  /* children and attributes */
  cptr = &copy->r.children;
  for (oc = orig->r.children; oc; oc = oc->next) {
    *cptr = expr_copy(oc);
    cptr = &(*cptr)->next;
  }

  cptr = &copy->r.attributes;
  for (oc = orig->r.attributes; oc; oc = oc->next) {
    *cptr = expr_copy(oc);
    cptr = &(*cptr)->next;
  }

  /* don't want to copy these properties - they're unique to the instance */
  copy->restype = RESTYPE_UNKNOWN;
  copy->derivatives = NULL;
  copy->called = 0;

  return copy;
}
Пример #9
0
struct expr *expr_copy(const struct expr *org)
{
	struct expr *e;

	if (!org)
		return NULL;

	e = xmalloc(sizeof(*org));
	memcpy(e, org, sizeof(*org));
	switch (org->type) {
	case E_SYMBOL:
		e->left = org->left;
		break;
	case E_NOT:
		e->left.expr = expr_copy(org->left.expr);
		break;
	case E_EQUAL:
	case E_GEQ:
	case E_GTH:
	case E_LEQ:
	case E_LTH:
	case E_UNEQUAL:
		e->left.sym = org->left.sym;
		e->right.sym = org->right.sym;
		break;
	case E_AND:
	case E_OR:
	case E_LIST:
		e->left.expr = expr_copy(org->left.expr);
		e->right.expr = expr_copy(org->right.expr);
		break;
	default:
		printf("can't copy type %d\n", e->type);
		free(e);
		e = NULL;
		break;
	}

	return e;
}
Пример #10
0
int
expr_eq(struct expr *e1, struct expr *e2)
{
    int res, old_count;

    if (e1->type != e2->type)
	return 0;
    switch (e1->type) {
    case E_EQUAL:
    case E_UNEQUAL:
	return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
    case E_SYMBOL:
	return e1->left.sym == e2->left.sym;
    case E_NOT:
	return expr_eq(e1->left.expr, e2->left.expr);
    case E_AND:
    case E_OR:
	e1 = expr_copy(e1);
	e2 = expr_copy(e2);
	old_count = trans_count;
	expr_eliminate_eq(&e1, &e2);
	res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
	       e1->left.sym == e2->left.sym);
	expr_free(e1);
	expr_free(e2);
	trans_count = old_count;
	return res;
    case E_CHOICE:
    case E_NONE:
	/* panic */ ;
    }

    print_expr(0, e1, 0);
    printf(" = ");
    print_expr(0, e2, 0);
    printf(" ?\n");

    return 0;
}
Пример #11
0
/*
 * Given expression `e1' and `e2', returns the leaf of the longest
 * sub-expression of `e1' not containing 'e2.
 */
struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2)
{
	struct expr *ret;

	switch (e1->type) {
	case E_OR:
		return expr_alloc_and(
		    expr_simplify_unmet_dep(e1->left.expr, e2),
		    expr_simplify_unmet_dep(e1->right.expr, e2));
	case E_AND: {
		struct expr *e;
		e = expr_alloc_and(expr_copy(e1), expr_copy(e2));
		e = expr_eliminate_dups(e);
		ret = (!expr_eq(e, e1)) ? e1 : NULL;
		expr_free(e);
		break;
		}
	default:
		ret = e1;
		break;
	}

	return expr_get_leftmost_symbol(ret);
}
Пример #12
0
struct expr *expr_copy(struct expr const *expr) {
    struct expr *copy = malloc(sizeof(*copy));
    
    switch (copy->type = expr->type) {
    case EXPR_NUMBER:
        copy->number = expr->number;
        break;
    case EXPR_IDENTIFIER:
        copy->identifier = strdup(expr->identifier);
        break;
    case EXPR_STRING:
        copy->string = strdup(expr->string);
        break;
    case EXPR_UNARY:
        copy->unary.type = expr->unary.type;
        copy->unary.arg = expr_copy(expr->unary.arg);
        break;
    case EXPR_BINARY:
        copy->binary.type = expr->binary.type;
        copy->binary.lhs = expr_copy(expr->binary.lhs);
        copy->binary.rhs = expr_copy(expr->binary.rhs);
        break;
    case EXPR_LIST:
        copy->list = expr_list_copy(expr->list);
        break;
    case EXPR_DICT:
        copy->dict = expr_list_copy(expr->dict);
        break;
    case EXPR_ATTR:
        copy->attr.expr = expr_copy(expr->attr.expr);
        copy->attr.identifier = strdup(expr->attr.identifier);
        break;
    }

    return copy;
}
Пример #13
0
stmt_ty *
stmt_command_new(expr_list_ty *args, expr_list_ty *flags, expr_ty *input,
                 expr_position_ty *pp)
{
    stmt_ty         *sp;
    stmt_command_ty *this;

    trace(("stmt_command_new()\n{\n"));
    sp = stmt_private_new(&method);
    this = (stmt_command_ty *)sp;

    expr_list_copy_constructor(&this->args, args);
    expr_list_copy_constructor(&this->flags, flags);
    this->input = (input ? expr_copy(input) : (expr_ty *)0);
    expr_position_copy_constructor(&this->pos, pp);

    trace(("return %8.8lX;\n", (long)sp));
    trace(("}\n"));
    return sp;
}
Пример #14
0
struct expr *expr_unary(enum expr_unary_type type, struct expr const *arg) {
    struct expr *expr = expr_alloc(EXPR_UNARY);
    expr->unary.type = type;
    expr->unary.arg = expr_copy(arg);
    return expr;
}
Пример #15
0
// recursively copy all the way down.
struct type * type_copy(struct type *t){
	if(!t) return NULL;
	struct type * temp = type_create(t->kind, param_list_copy(t->params), type_copy(t->subtype), expr_copy(t->opt_expr));
	return temp;
}
Пример #16
0
void menu_finalize(struct menu *parent)
{
    struct menu *menu, *last_menu;
    struct symbol *sym;
    struct property *prop;
    struct expr *parentdep, *basedep, *dep, *dep2, **ep;

    sym = parent->sym;
    if (parent->list) {
        if (sym && sym_is_choice(sym)) {
            /* find the first choice value and find out choice type */
            for (menu = parent->list; menu; menu = menu->next) {
                if (menu->sym) {
                    current_entry = parent;
                    menu_set_type(menu->sym->type);
                    current_entry = menu;
                    menu_set_type(sym->type);
                    break;
                }
            }
            parentdep = expr_alloc_symbol(sym);
        } else if (parent->prompt) {
            parentdep = parent->prompt->visible.expr;
        } else {
            parentdep = parent->dep;
        }

        for (menu = parent->list; menu; menu = menu->next) {
            basedep = expr_transform(menu->dep);
            basedep = expr_alloc_and(expr_copy(parentdep), basedep);
            basedep = expr_eliminate_dups(basedep);
            menu->dep = basedep;
            if (menu->sym) {
                prop = menu->sym->prop;
            } else {
                prop = menu->prompt;
            }
            for (; prop; prop = prop->next) {
                if (prop->menu != menu) {
                    continue;
                }
                dep = expr_transform(prop->visible.expr);
                dep = expr_alloc_and(expr_copy(basedep), dep);
                dep = expr_eliminate_dups(dep);
                if (menu->sym && menu->sym->type != S_TRISTATE) {
                    dep = expr_trans_bool(dep);
                }
                prop->visible.expr = dep;
                if (prop->type == P_SELECT) {
                    struct symbol *es = prop_get_symbol(prop);
                    es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
                                                     expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
                }
            }
        }
        for (menu = parent->list; menu; menu = menu->next) {
            menu_finalize(menu);
        }
    } else if (sym) {
        basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
        basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
        basedep = expr_eliminate_dups(expr_transform(basedep));
        last_menu = NULL;
        for (menu = parent->next; menu; menu = menu->next) {
            dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
            if (!expr_contains_symbol(dep, sym)) {
                break;
            }
            if (expr_depends_symbol(dep, sym)) {
                goto next;
            }
            dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
            dep = expr_eliminate_dups(expr_transform(dep));
            dep2 = expr_copy(basedep);
            expr_eliminate_eq(&dep, &dep2);
            expr_free(dep);
            if (!expr_is_yes(dep2)) {
                expr_free(dep2);
                break;
            }
            expr_free(dep2);
next:
            menu_finalize(menu);
            menu->parent = parent;
            last_menu = menu;
        }
        if (last_menu) {
            parent->list = parent->next;
            parent->next = last_menu->next;
            last_menu->next = NULL;
        }
    }
    for (menu = parent->list; menu; menu = menu->next) {
        if (sym && sym_is_choice(sym) && menu->sym) {
            menu->sym->flags |= SYMBOL_CHOICEVAL;
            if (!menu->prompt) {
                menu_warn(menu, "choice value must have a prompt");
            }
            for (prop = menu->sym->prop; prop; prop = prop->next) {
                if (prop->type == P_PROMPT && prop->menu != menu) {
                    prop_warn(prop, "choice values "
                              "currently only support a "
                              "single prompt");
                }
                if (prop->type == P_DEFAULT)
                    prop_warn(prop, "defaults for choice "
                              "values not supported");
            }
            current_entry = menu;
            menu_set_type(sym->type);
            menu_add_symbol(P_CHOICE, sym, NULL);
            prop = sym_get_choice_prop(sym);
            for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
                ;
            *ep = expr_alloc_one(E_CHOICE, NULL);
            (*ep)->right.sym = menu->sym;
        }
        if (menu->list && (!menu->prompt || !menu->prompt->text)) {
            for (last_menu = menu->list; ; last_menu = last_menu->next) {
                last_menu->parent = parent;
                if (!last_menu->next) {
                    break;
                }
            }
            last_menu->next = menu->next;
            menu->next = menu->list;
            menu->list = NULL;
        }
    }

    if (sym && !(sym->flags & SYMBOL_WARNED)) {
        if (sym->type == S_UNKNOWN)
            menu_warn(parent, "config symbol defined "
                      "without type\n");

        if (sym_is_choice(sym) && !parent->prompt) {
            menu_warn(parent, "choice must have a prompt\n");
        }

        /* Check properties connected to this symbol */
        sym_check_prop(sym);
        sym->flags |= SYMBOL_WARNED;
    }

    if (sym && !sym_is_optional(sym) && parent->prompt) {
        sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
                                          expr_alloc_and(parent->prompt->visible.expr,
                                                         expr_alloc_symbol(&symbol_mod)));
    }
}
Пример #17
0
struct expr *
expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
{
    struct expr *e1, *e2;

    if (!e) {
	e = expr_alloc_symbol(sym);
	if (type == E_UNEQUAL)
	    e = expr_alloc_one(E_NOT, e);
	return e;
    }
    switch (e->type) {
    case E_AND:
	e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
	e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
	if (sym == &symbol_yes)
	    e = expr_alloc_two(E_AND, e1, e2);
	if (sym == &symbol_no)
	    e = expr_alloc_two(E_OR, e1, e2);
	if (type == E_UNEQUAL)
	    e = expr_alloc_one(E_NOT, e);
	return e;
    case E_OR:
	e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
	e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
	if (sym == &symbol_yes)
	    e = expr_alloc_two(E_OR, e1, e2);
	if (sym == &symbol_no)
	    e = expr_alloc_two(E_AND, e1, e2);
	if (type == E_UNEQUAL)
	    e = expr_alloc_one(E_NOT, e);
	return e;
    case E_NOT:
	return expr_trans_compare(e->left.expr,
				  type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
    case E_UNEQUAL:
    case E_EQUAL:
	if (type == E_EQUAL) {
	    if (sym == &symbol_yes)
		return expr_copy(e);
	    if (sym == &symbol_mod)
		return expr_alloc_symbol(&symbol_no);
	    if (sym == &symbol_no)
		return expr_alloc_one(E_NOT, expr_copy(e));
	} else {
	    if (sym == &symbol_yes)
		return expr_alloc_one(E_NOT, expr_copy(e));
	    if (sym == &symbol_mod)
		return expr_alloc_symbol(&symbol_yes);
	    if (sym == &symbol_no)
		return expr_copy(e);
	}
	break;
    case E_SYMBOL:
	return expr_alloc_comp(type, e->left.sym, sym);
    case E_CHOICE:
    case E_NONE:
	/* panic */ ;
    }
    return NULL;
}
Пример #18
0
/*
 * e1 || e2 -> ?
 */
struct expr *
expr_join_or(struct expr *e1, struct expr *e2)
{
    struct expr *tmp;
    struct symbol *sym1, *sym2;

    if (expr_eq(e1, e2))
	return expr_copy(e1);
    if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL
	&& e1->type != E_NOT)
	return NULL;
    if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL
	&& e2->type != E_NOT)
	return NULL;
    if (e1->type == E_NOT) {
	tmp = e1->left.expr;
	if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL
	    && tmp->type != E_SYMBOL)
	    return NULL;
	sym1 = tmp->left.sym;
    } else
	sym1 = e1->left.sym;
    if (e2->type == E_NOT) {
	if (e2->left.expr->type != E_SYMBOL)
	    return NULL;
	sym2 = e2->left.expr->left.sym;
    } else
	sym2 = e2->left.sym;
    if (sym1 != sym2)
	return NULL;
    if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
	return NULL;
    if (sym1->type == S_TRISTATE) {
	if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
	    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
	     (e1->right.sym == &symbol_mod
	      && e2->right.sym == &symbol_yes))) {
	    // (a='y') || (a='m') -> (a!='n')
	    return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
	}
	if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
	    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
	     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
	    // (a='y') || (a='n') -> (a!='m')
	    return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
	}
	if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
	    ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
	     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
	    // (a='m') || (a='n') -> (a!='y')
	    return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
	}
    }
    if (sym1->type == S_BOOLEAN && sym1 == sym2) {
	if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL
	     && e2->type == E_SYMBOL) || (e2->type == E_NOT
					  && e2->left.expr->type == E_SYMBOL
					  && e1->type == E_SYMBOL))
	    return expr_alloc_symbol(&symbol_yes);
    }

    printf("optimize ");
    print_expr(0, e1, 0);
    printf(" || ");
    print_expr(0, e2, 0);
    printf(" ?\n");
    return NULL;
}
Пример #19
0
void
menu_finalize(struct menu *parent)
{
    struct menu *menu, *last_menu;
    struct symbol *sym;
    struct property *prop;
    struct expr *parentdep, *basedep, *dep, *dep2;

    sym = parent->sym;
    if (parent->list) {
	if (sym && sym_is_choice(sym)) {
	    /* find the first choice value and find out choice type */
	    for (menu = parent->list; menu; menu = menu->next) {
		if (menu->sym) {
		    current_entry = parent;
		    menu_set_type(menu->sym->type);
		    current_entry = menu;
		    menu_set_type(sym->type);
		    break;
		}
	    }
	    parentdep = expr_alloc_symbol(sym);
	} else if (parent->prompt)
	    parentdep = E_EXPR(parent->prompt->visible);
	else
	    parentdep = parent->dep;

	for (menu = parent->list; menu; menu = menu->next) {
	    basedep = expr_transform(menu->dep);
	    basedep = expr_alloc_and(expr_copy(parentdep), basedep);
	    basedep = expr_eliminate_dups(basedep);
	    menu->dep = basedep;
	    if (menu->sym)
		prop = menu->sym->prop;
	    else
		prop = menu->prompt;
	    for (; prop; prop = prop->next) {
		if (prop->menu != menu)
		    continue;
		dep = expr_transform(E_EXPR(prop->visible));
		dep = expr_alloc_and(expr_copy(basedep), dep);
		dep = expr_eliminate_dups(dep);
		if (menu->sym && menu->sym->type != S_TRISTATE)
		    dep = expr_trans_bool(dep);
		E_EXPR(prop->visible) = dep;
	    }
	}
	for (menu = parent->list; menu; menu = menu->next)
	    menu_finalize(menu);
    } else if (sym && parent->prompt) {
	basedep = E_EXPR(parent->prompt->visible);
	basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
	basedep = expr_eliminate_dups(expr_transform(basedep));
	last_menu = NULL;
	for (menu = parent->next; menu; menu = menu->next) {
	    dep = menu->prompt ? E_EXPR(menu->prompt->visible) : menu->dep;
	    if (!expr_contains_symbol(dep, sym))
		break;
	    if (expr_depends_symbol(dep, sym))
		goto next;
	    dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
	    dep = expr_eliminate_dups(expr_transform(dep));
	    dep2 = expr_copy(basedep);
	    expr_eliminate_eq(&dep, &dep2);
	    expr_free(dep);
	    if (!expr_is_yes(dep2)) {
		expr_free(dep2);
		break;
	    }
	    expr_free(dep2);
	  next:
	    menu_finalize(menu);
	    menu->parent = parent;
	    last_menu = menu;
	}
	if (last_menu) {
	    parent->list = parent->next;
	    parent->next = last_menu->next;
	    last_menu->next = NULL;
	}
    }
    for (menu = parent->list; menu; menu = menu->next) {
	if (sym && sym_is_choice(sym) && menu->sym) {
	    menu->sym->flags |= SYMBOL_CHOICEVAL;
	    current_entry = menu;
	    menu_set_type(sym->type);
	    menu_add_prop(P_CHOICE, NULL, parent->sym, NULL);
	    prop = sym_get_choice_prop(parent->sym);
	    //dep = expr_alloc_one(E_CHOICE, dep);
	    //dep->right.sym = menu->sym;
	    prop->dep = expr_alloc_one(E_CHOICE, prop->dep);
	    prop->dep->right.sym = menu->sym;
	}
	if (menu->list && (!menu->prompt || !menu->prompt->text)) {
	    for (last_menu = menu->list;; last_menu = last_menu->next) {
		last_menu->parent = parent;
		if (!last_menu->next)
		    break;
	    }
	    last_menu->next = menu->next;
	    menu->next = menu->list;
	    menu->list = NULL;
	}
    }
}
Пример #20
0
void menu_finalize(struct menu *parent)
{
	struct menu *menu, *last_menu;
	struct symbol *sym;
	struct property *prop;
	struct expr *parentdep, *basedep, *dep, *dep2, **ep;

	sym = parent->sym;
	if (parent->list) {
		/*
		 * This menu node has children. We (recursively) process them
		 * and propagate parent dependencies before moving on.
		 */

		if (sym && sym_is_choice(sym)) {
			if (sym->type == S_UNKNOWN) {
				/* find the first choice value to find out choice type */
				current_entry = parent;
				for (menu = parent->list; menu; menu = menu->next) {
					if (menu->sym && menu->sym->type != S_UNKNOWN) {
						menu_set_type(menu->sym->type);
						break;
					}
				}
			}
			/* set the type of the remaining choice values */
			for (menu = parent->list; menu; menu = menu->next) {
				current_entry = menu;
				if (menu->sym && menu->sym->type == S_UNKNOWN)
					menu_set_type(sym->type);
			}

			/*
			 * Use the choice itself as the parent dependency of
			 * the contained items. This turns the mode of the
			 * choice into an upper bound on the visibility of the
			 * choice value symbols.
			 */
			parentdep = expr_alloc_symbol(sym);
		} else if (parent->prompt)
			/* Menu node for 'menu' */
			parentdep = parent->prompt->visible.expr;
		else
			/* Menu node for 'if' */
			parentdep = parent->dep;

		/* For each child menu node... */
		for (menu = parent->list; menu; menu = menu->next) {
			/*
			 * Propagate parent dependencies to the child menu
			 * node, also rewriting and simplifying expressions
			 */
			basedep = rewrite_m(menu->dep);
			basedep = expr_transform(basedep);
			basedep = expr_alloc_and(expr_copy(parentdep), basedep);
			basedep = expr_eliminate_dups(basedep);
			menu->dep = basedep;

			if (menu->sym)
				/*
				 * Note: For symbols, all prompts are included
				 * too in the symbol's own property list
				 */
				prop = menu->sym->prop;
			else
				/*
				 * For non-symbol menu nodes, we just need to
				 * handle the prompt
				 */
				prop = menu->prompt;

			/* For each property... */
			for (; prop; prop = prop->next) {
				if (prop->menu != menu)
					/*
					 * Two possibilities:
					 *
					 * 1. The property lacks dependencies
					 *    and so isn't location-specific,
					 *    e.g. an 'option'
					 *
					 * 2. The property belongs to a symbol
					 *    defined in multiple locations and
					 *    is from some other location. It
					 *    will be handled there in that
					 *    case.
					 *
					 * Skip the property.
					 */
					continue;

				/*
				 * Propagate parent dependencies to the
				 * property's condition, rewriting and
				 * simplifying expressions at the same time
				 */
				dep = rewrite_m(prop->visible.expr);
				dep = expr_transform(dep);
				dep = expr_alloc_and(expr_copy(basedep), dep);
				dep = expr_eliminate_dups(dep);
				if (menu->sym && menu->sym->type != S_TRISTATE)
					dep = expr_trans_bool(dep);
				prop->visible.expr = dep;

				/*
				 * Handle selects and implies, which modify the
				 * dependencies of the selected/implied symbol
				 */
				if (prop->type == P_SELECT) {
					struct symbol *es = prop_get_symbol(prop);
					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
				} else if (prop->type == P_IMPLY) {
					struct symbol *es = prop_get_symbol(prop);
					es->implied.expr = expr_alloc_or(es->implied.expr,
							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
				}
			}
		}

		if (sym && sym_is_choice(sym))
			expr_free(parentdep);

		/*
		 * Recursively process children in the same fashion before
		 * moving on
		 */
		for (menu = parent->list; menu; menu = menu->next)
			menu_finalize(menu);
	} else if (sym) {
		/*
		 * Automatic submenu creation. If sym is a symbol and A, B, C,
		 * ... are consecutive items (symbols, menus, ifs, etc.) that
		 * all depend on sym, then the following menu structure is
		 * created:
		 *
		 *	sym
		 *	 +-A
		 *	 +-B
		 *	 +-C
		 *	 ...
		 *
		 * This also works recursively, giving the following structure
		 * if A is a symbol and B depends on A:
		 *
		 *	sym
		 *	 +-A
		 *	 | +-B
		 *	 +-C
		 *	 ...
		 */

		basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
		basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
		basedep = expr_eliminate_dups(expr_transform(basedep));

		/* Examine consecutive elements after sym */
		last_menu = NULL;
		for (menu = parent->next; menu; menu = menu->next) {
			dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
			if (!expr_contains_symbol(dep, sym))
				/* No dependency, quit */
				break;
			if (expr_depends_symbol(dep, sym))
				/* Absolute dependency, put in submenu */
				goto next;

			/*
			 * Also consider it a dependency on sym if our
			 * dependencies contain sym and are a "superset" of
			 * sym's dependencies, e.g. '(sym || Q) && R' when sym
			 * depends on R.
			 *
			 * Note that 'R' might be from an enclosing menu or if,
			 * making this a more common case than it might seem.
			 */
			dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
			dep = expr_eliminate_dups(expr_transform(dep));
			dep2 = expr_copy(basedep);
			expr_eliminate_eq(&dep, &dep2);
			expr_free(dep);
			if (!expr_is_yes(dep2)) {
				/* Not superset, quit */
				expr_free(dep2);
				break;
			}
			/* Superset, put in submenu */
			expr_free(dep2);
		next:
			menu_finalize(menu);
			menu->parent = parent;
			last_menu = menu;
		}
		expr_free(basedep);
		if (last_menu) {
			parent->list = parent->next;
			parent->next = last_menu->next;
			last_menu->next = NULL;
		}

		sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
	}
	for (menu = parent->list; menu; menu = menu->next) {
		if (sym && sym_is_choice(sym) &&
		    menu->sym && !sym_is_choice_value(menu->sym)) {
			current_entry = menu;
			menu->sym->flags |= SYMBOL_CHOICEVAL;
			if (!menu->prompt)
				menu_warn(menu, "choice value must have a prompt");
			for (prop = menu->sym->prop; prop; prop = prop->next) {
				if (prop->type == P_DEFAULT)
					prop_warn(prop, "defaults for choice "
						  "values not supported");
				if (prop->menu == menu)
					continue;
				if (prop->type == P_PROMPT &&
				    prop->menu->parent->sym != sym)
					prop_warn(prop, "choice value used outside its choice group");
			}
			/* Non-tristate choice values of tristate choices must
			 * depend on the choice being set to Y. The choice
			 * values' dependencies were propagated to their
			 * properties above, so the change here must be re-
			 * propagated.
			 */
			if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
				basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
				menu->dep = expr_alloc_and(basedep, menu->dep);
				for (prop = menu->sym->prop; prop; prop = prop->next) {
					if (prop->menu != menu)
						continue;
					prop->visible.expr = expr_alloc_and(expr_copy(basedep),
									    prop->visible.expr);
				}
			}
			menu_add_symbol(P_CHOICE, sym, NULL);
			prop = sym_get_choice_prop(sym);
			for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
				;
			*ep = expr_alloc_one(E_LIST, NULL);
			(*ep)->right.sym = menu->sym;
		}

		/*
		 * This code serves two purposes:
		 *
		 * (1) Flattening 'if' blocks, which do not specify a submenu
		 *     and only add dependencies.
		 *
		 *     (Automatic submenu creation might still create a submenu
		 *     from an 'if' before this code runs.)
		 *
		 * (2) "Undoing" any automatic submenus created earlier below
		 *     promptless symbols.
		 *
		 * Before:
		 *
		 *	A
		 *	if ... (or promptless symbol)
		 *	 +-B
		 *	 +-C
		 *	D
		 *
		 * After:
		 *
		 *	A
		 *	if ... (or promptless symbol)
		 *	B
		 *	C
		 *	D
		 */
		if (menu->list && (!menu->prompt || !menu->prompt->text)) {
			for (last_menu = menu->list; ; last_menu = last_menu->next) {
				last_menu->parent = parent;
				if (!last_menu->next)
					break;
			}
			last_menu->next = menu->next;
			menu->next = menu->list;
			menu->list = NULL;
		}
	}

	if (sym && !(sym->flags & SYMBOL_WARNED)) {
		if (sym->type == S_UNKNOWN)
			menu_warn(parent, "config symbol defined without type");

		if (sym_is_choice(sym) && !parent->prompt)
			menu_warn(parent, "choice must have a prompt");

		/* Check properties connected to this symbol */
		sym_check_prop(sym);
		sym->flags |= SYMBOL_WARNED;
	}

	/*
	 * For non-optional choices, add a reverse dependency (corresponding to
	 * a select) of '<visibility> && m'. This prevents the user from
	 * setting the choice mode to 'n' when the choice is visible.
	 *
	 * This would also work for non-choice symbols, but only non-optional
	 * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented
	 * as a type of symbol.
	 */
	if (sym && !sym_is_optional(sym) && parent->prompt) {
		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
				expr_alloc_and(parent->prompt->visible.expr,
					expr_alloc_symbol(&symbol_mod)));
	}
}
Пример #21
0
struct expr *expr_attr(struct expr const *lhs, char const *rhs) {
    struct expr *expr = expr_alloc(EXPR_ATTR);
    expr->attr.expr = expr_copy(lhs);
    expr->attr.identifier = strdup(rhs);
    return expr;
}
Пример #22
0
struct stmt *stmt_assign(char const *identifier, struct expr const *expr) {
    struct stmt *stmt = stmt_alloc(STMT_ASSIGN);
    stmt->assign.identifier = strdup(identifier);
    stmt->assign.value = expr_copy(expr);
    return stmt;
}
Пример #23
0
static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
{
	struct expr *tmp;
	struct symbol *sym1, *sym2;

	if (expr_eq(e1, e2))
		return expr_copy(e1);
	if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
		return NULL;
	if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
		return NULL;
	if (e1->type == E_NOT) {
		tmp = e1->left.expr;
		if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
			return NULL;
		sym1 = tmp->left.sym;
	} else
		sym1 = e1->left.sym;
	if (e2->type == E_NOT) {
		if (e2->left.expr->type != E_SYMBOL)
			return NULL;
		sym2 = e2->left.expr->left.sym;
	} else
		sym2 = e2->left.sym;
	if (sym1 != sym2)
		return NULL;
	if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
		return NULL;

	if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
	    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
		// (a) && (a='y') -> (a='y')
		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);

	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
		// (a) && (a!='n') -> (a)
		return expr_alloc_symbol(sym1);

	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
		// (a) && (a!='m') -> (a='y')
		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);

	if (sym1->type == S_TRISTATE) {
		if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
			sym2 = e1->right.sym;
			if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
				return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
							     : expr_alloc_symbol(&symbol_no);
		}
		if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
			sym2 = e2->right.sym;
			if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
				return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
							     : expr_alloc_symbol(&symbol_no);
		}
		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
			// (a!='y') && (a!='n') -> (a='m')
			return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);

		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
			    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
			// (a!='y') && (a!='m') -> (a='n')
			return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);

		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
			   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
			// (a!='m') && (a!='n') -> (a='m')
			return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);

		if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
		    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
		    (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
		    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
			return NULL;
	}

	if (DEBUG_EXPR) {
		printf("optimize (");
		expr_fprint(e1, stdout);
		printf(") && (");
		expr_fprint(e2, stdout);
		printf(")?\n");
	}
	return NULL;
}
Пример #24
0
void menu_finalize(struct menu *parent)
{
	struct menu *menu, *last_menu;
	struct symbol *sym;
	struct property *prop;
	struct expr *parentdep, *basedep, *dep, *dep2, **ep;

	sym = parent->sym;
	if (parent->list) {
		if (sym && sym_is_choice(sym)) {
			if (sym->type == S_UNKNOWN) {
				/* find the first choice value to find out choice type */
				current_entry = parent;
				for (menu = parent->list; menu; menu = menu->next) {
					if (menu->sym && menu->sym->type != S_UNKNOWN) {
						menu_set_type(menu->sym->type);
						break;
					}
				}
			}
			if (parent->prompt &&
			    !expr_is_yes(parent->prompt->visible.expr)) {
				parent->visibility = expr_alloc_and (parent->visibility,
								     parent->prompt->visible.expr);
			}
			/* set the type of the remaining choice values */
			for (menu = parent->list; menu; menu = menu->next) {
				current_entry = menu;
				if (menu->sym && menu->sym->type == S_UNKNOWN)
					menu_set_type(sym->type);
			}
			parentdep = expr_alloc_symbol(sym);
		} else if (parent->prompt)
			parentdep = parent->prompt->visible.expr;
		else
			parentdep = parent->dep;

		for (menu = parent->list; menu; menu = menu->next) {
			basedep = expr_transform(menu->dep);
			basedep = expr_alloc_and(expr_copy(parentdep), basedep);
			basedep = expr_eliminate_dups(basedep);
			menu->dep = basedep;
			if (menu->sym)
				prop = menu->sym->prop;
			else
				prop = menu->prompt;
			for (; prop; prop = prop->next) {
				if (prop->menu != menu)
					continue;
				dep = expr_transform(prop->visible.expr);
				dep = expr_alloc_and(expr_copy(basedep), dep);
				dep = expr_eliminate_dups(dep);
				if (menu->sym && menu->sym->type != S_TRISTATE)
					dep = expr_trans_bool(dep);
				prop->visible.expr = dep;
				if (prop->type == P_SELECT) {
					struct symbol *es = prop_get_symbol(prop);
					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
				}
			}
		}
		for (menu = parent->list; menu; menu = menu->next)
			menu_finalize(menu);
	} else if (sym) {
		basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
		basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
		basedep = expr_eliminate_dups(expr_transform(basedep));
		last_menu = NULL;
		for (menu = parent->next; menu; menu = menu->next) {
			dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
			if (!expr_contains_symbol(dep, sym))
				break;
			if (expr_depends_symbol(dep, sym))
				goto next;
			dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
			dep = expr_eliminate_dups(expr_transform(dep));
			dep2 = expr_copy(basedep);
			expr_eliminate_eq(&dep, &dep2);
			expr_free(dep);
			if (!expr_is_yes(dep2)) {
				expr_free(dep2);
				break;
			}
			expr_free(dep2);
		next:
			menu_finalize(menu);
			menu->parent = parent;
			last_menu = menu;
		}
		if (last_menu) {
			parent->list = parent->next;
			parent->next = last_menu->next;
			last_menu->next = NULL;
		}

		sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
	}
	for (menu = parent->list; menu; menu = menu->next) {
		if (sym && sym_is_choice(sym) &&
		    menu->sym && !sym_is_choice_value(menu->sym)) {
			current_entry = menu;
			menu->sym->flags |= SYMBOL_CHOICEVAL;
			if (!menu->prompt)
				menu_warn(menu, "choice value must have a prompt");
			for (prop = menu->sym->prop; prop; prop = prop->next) {
				if (prop->type == P_DEFAULT)
					prop_warn(prop, "defaults for choice "
						  "values not supported");
				if (prop->menu == menu)
					continue;
				if (prop->type == P_PROMPT &&
				    prop->menu->parent->sym != sym)
					prop_warn(prop, "choice value used outside its choice group");
			}
			/* Non-tristate choice values of tristate choices must
			 * depend on the choice being set to Y. The choice
			 * values' dependencies were propagated to their
			 * properties above, so the change here must be re-
			 * propagated.
			 */
			if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
				basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
				menu->dep = expr_alloc_and(basedep, menu->dep);
				for (prop = menu->sym->prop; prop; prop = prop->next) {
					if (prop->menu != menu)
						continue;
					prop->visible.expr = expr_alloc_and(expr_copy(basedep),
									    prop->visible.expr);
				}
			}
			menu_add_symbol(P_CHOICE, sym, NULL);
			prop = sym_get_choice_prop(sym);
			for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
				;
			*ep = expr_alloc_one(E_LIST, NULL);
			(*ep)->right.sym = menu->sym;
		}
		if (menu->list && (!menu->prompt || !menu->prompt->text)) {
			for (last_menu = menu->list; ; last_menu = last_menu->next) {
				last_menu->parent = parent;
				if (!last_menu->next)
					break;
			}
			last_menu->next = menu->next;
			menu->next = menu->list;
			menu->list = NULL;
		}
	}

	if (sym && !(sym->flags & SYMBOL_WARNED)) {
		if (sym->type == S_UNKNOWN)
			menu_warn(parent, "config symbol defined without type");

		if (sym_is_choice(sym) && !parent->prompt)
			menu_warn(parent, "choice must have a prompt");

		/* Check properties connected to this symbol */
		sym_check_prop(sym);
		sym->flags |= SYMBOL_WARNED;
	}

	if (sym && !sym_is_optional(sym) && parent->prompt) {
		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
				expr_alloc_and(parent->prompt->visible.expr,
					expr_alloc_symbol(&symbol_mod)));
	}
}
Пример #25
0
struct stmt *stmt_print(struct expr const *expr) {
    struct stmt *stmt = stmt_alloc(STMT_PRINT);
    stmt->print = expr_copy(expr);
    return stmt;
}